diff options
| author | James Munns <[email protected]> | 2025-12-05 20:45:59 +0100 |
|---|---|---|
| committer | James Munns <[email protected]> | 2025-12-05 20:45:59 +0100 |
| commit | 5d8f3a3d18eda339e258193295cf332d7e01882e (patch) | |
| tree | cb20b9e64c682a1157896852a1123db38a3cf4e0 /embassy-mcxa/src/dma.rs | |
| parent | 787bf84963ecd32306b6b2993504b2196f71cf72 (diff) | |
Clean up some common PAC operations using helper methods
Diffstat (limited to 'embassy-mcxa/src/dma.rs')
| -rw-r--r-- | embassy-mcxa/src/dma.rs | 423 |
1 files changed, 147 insertions, 276 deletions
diff --git a/embassy-mcxa/src/dma.rs b/embassy-mcxa/src/dma.rs index d563c2e29..b3ea94dbf 100644 --- a/embassy-mcxa/src/dma.rs +++ b/embassy-mcxa/src/dma.rs | |||
| @@ -581,7 +581,6 @@ pub struct DmaChannel<C: Channel> { | |||
| 581 | // - [`write_to_peripheral()`](DmaChannel::write_to_peripheral) - Memory-to-peripheral with custom eDMA TCD block | 581 | // - [`write_to_peripheral()`](DmaChannel::write_to_peripheral) - Memory-to-peripheral with custom eDMA TCD block |
| 582 | // - [`read_from_peripheral()`](DmaChannel::read_from_peripheral) - Peripheral-to-memory with custom eDMA TCD block | 582 | // - [`read_from_peripheral()`](DmaChannel::read_from_peripheral) - Peripheral-to-memory with custom eDMA TCD block |
| 583 | // - [`mem_to_mem()`](DmaChannel::mem_to_mem) - Memory-to-memory using default eDMA TCD block | 583 | // - [`mem_to_mem()`](DmaChannel::mem_to_mem) - Memory-to-memory using default eDMA TCD block |
| 584 | // - [`transfer_mem_to_mem()`](DmaChannel::transfer_mem_to_mem) - Memory-to-memory with custom eDMA TCD block | ||
| 585 | // | 584 | // |
| 586 | // The `Transfer` manages the DMA lifecycle automatically: | 585 | // The `Transfer` manages the DMA lifecycle automatically: |
| 587 | // - Enables channel request | 586 | // - Enables channel request |
| @@ -673,6 +672,87 @@ impl<C: Channel> DmaChannel<C> { | |||
| 673 | edma.tcd(C::INDEX) | 672 | edma.tcd(C::INDEX) |
| 674 | } | 673 | } |
| 675 | 674 | ||
| 675 | fn clear_tcd(t: &'static pac::edma_0_tcd0::Tcd) { | ||
| 676 | // Full TCD reset following NXP SDK pattern (EDMA_TcdResetExt). | ||
| 677 | // Reset ALL TCD registers to 0 to clear any stale configuration from | ||
| 678 | // previous transfers. This is critical when reusing a channel. | ||
| 679 | t.tcd_saddr().write(|w| unsafe { w.saddr().bits(0) }); | ||
| 680 | t.tcd_soff().write(|w| unsafe { w.soff().bits(0) }); | ||
| 681 | t.tcd_attr().write(|w| unsafe { w.bits(0) }); | ||
| 682 | t.tcd_nbytes_mloffno().write(|w| unsafe { w.nbytes().bits(0) }); | ||
| 683 | t.tcd_slast_sda().write(|w| unsafe { w.slast_sda().bits(0) }); | ||
| 684 | t.tcd_daddr().write(|w| unsafe { w.daddr().bits(0) }); | ||
| 685 | t.tcd_doff().write(|w| unsafe { w.doff().bits(0) }); | ||
| 686 | t.tcd_citer_elinkno().write(|w| unsafe { w.bits(0) }); | ||
| 687 | t.tcd_dlast_sga().write(|w| unsafe { w.dlast_sga().bits(0) }); | ||
| 688 | t.tcd_csr().write(|w| unsafe { w.bits(0) }); // Clear CSR completely | ||
| 689 | t.tcd_biter_elinkno().write(|w| unsafe { w.bits(0) }); | ||
| 690 | } | ||
| 691 | |||
| 692 | #[inline] | ||
| 693 | fn set_major_loop_ct_elinkno(t: &'static pac::edma_0_tcd0::Tcd, count: u16) { | ||
| 694 | t.tcd_biter_elinkno().write(|w| unsafe { w.biter().bits(count) }); | ||
| 695 | t.tcd_citer_elinkno().write(|w| unsafe { w.citer().bits(count) }); | ||
| 696 | } | ||
| 697 | |||
| 698 | #[inline] | ||
| 699 | fn set_minor_loop_ct_no_offsets(t: &'static pac::edma_0_tcd0::Tcd, count: u32) { | ||
| 700 | t.tcd_nbytes_mloffno().write(|w| unsafe { w.nbytes().bits(count) }); | ||
| 701 | } | ||
| 702 | |||
| 703 | #[inline] | ||
| 704 | fn set_no_final_adjustments(t: &'static pac::edma_0_tcd0::Tcd) { | ||
| 705 | // No source/dest adjustment after major loop | ||
| 706 | t.tcd_slast_sda().write(|w| unsafe { w.slast_sda().bits(0) }); | ||
| 707 | t.tcd_dlast_sga().write(|w| unsafe { w.dlast_sga().bits(0) }); | ||
| 708 | } | ||
| 709 | |||
| 710 | #[inline] | ||
| 711 | fn set_source_ptr<T>(t: &'static pac::edma_0_tcd0::Tcd, p: *const T) { | ||
| 712 | t.tcd_saddr().write(|w| unsafe { w.saddr().bits(p as u32) }); | ||
| 713 | } | ||
| 714 | |||
| 715 | #[inline] | ||
| 716 | fn set_source_increment(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) { | ||
| 717 | t.tcd_soff().write(|w| unsafe { w.soff().bits(sz.bytes() as u16) }); | ||
| 718 | } | ||
| 719 | |||
| 720 | #[inline] | ||
| 721 | fn set_source_fixed(t: &'static pac::edma_0_tcd0::Tcd) { | ||
| 722 | t.tcd_soff().write(|w| unsafe { w.soff().bits(0) }); | ||
| 723 | } | ||
| 724 | |||
| 725 | #[inline] | ||
| 726 | fn set_dest_ptr<T>(t: &'static pac::edma_0_tcd0::Tcd, p: *mut T) { | ||
| 727 | t.tcd_daddr().write(|w| unsafe { w.daddr().bits(p as u32) }); | ||
| 728 | } | ||
| 729 | |||
| 730 | #[inline] | ||
| 731 | fn set_dest_increment(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) { | ||
| 732 | t.tcd_doff().write(|w| unsafe { w.doff().bits(sz.bytes() as u16) }); | ||
| 733 | } | ||
| 734 | |||
| 735 | #[inline] | ||
| 736 | fn set_dest_fixed(t: &'static pac::edma_0_tcd0::Tcd) { | ||
| 737 | t.tcd_doff().write(|w| unsafe { w.doff().bits(0) }); | ||
| 738 | } | ||
| 739 | |||
| 740 | #[inline] | ||
| 741 | fn set_even_transfer_size(t: &'static pac::edma_0_tcd0::Tcd, sz: WordSize) { | ||
| 742 | let hw_size = sz.to_hw_size(); | ||
| 743 | t.tcd_attr().write(|w| unsafe { w.ssize().bits(hw_size).dsize().bits(hw_size) } ); | ||
| 744 | } | ||
| 745 | |||
| 746 | #[inline] | ||
| 747 | fn reset_channel_state(t: &'static pac::edma_0_tcd0::Tcd) { | ||
| 748 | // CSR: Resets to all zeroes (disabled), "done" is cleared by writing 1 | ||
| 749 | t.ch_csr().write(|w| w.done().clear_bit_by_one()); | ||
| 750 | // ES: Resets to all zeroes (disabled), "err" is cleared by writing 1 | ||
| 751 | t.ch_es().write(|w| w.err().clear_bit_by_one()); | ||
| 752 | // INT: Resets to all zeroes (disabled), "int" is cleared by writing 1 | ||
| 753 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 754 | } | ||
| 755 | |||
| 676 | /// Start an async transfer. | 756 | /// Start an async transfer. |
| 677 | /// | 757 | /// |
| 678 | /// The channel must already be configured. This enables the channel | 758 | /// The channel must already be configured. This enables the channel |
| @@ -716,32 +796,7 @@ impl<C: Channel> DmaChannel<C> { | |||
| 716 | /// | 796 | /// |
| 717 | /// The source and destination buffers must remain valid for the | 797 | /// The source and destination buffers must remain valid for the |
| 718 | /// duration of the transfer. | 798 | /// duration of the transfer. |
| 719 | pub unsafe fn mem_to_mem<W: Word>(&self, src: &[W], dst: &mut [W], options: TransferOptions) -> Transfer<'_> { | 799 | pub fn mem_to_mem<W: Word>(&self, src: &[W], dst: &mut [W], options: TransferOptions) -> Transfer<'_> { |
| 720 | self.transfer_mem_to_mem(src, dst, options) | ||
| 721 | } | ||
| 722 | |||
| 723 | /// Perform a memory-to-memory DMA transfer. | ||
| 724 | /// | ||
| 725 | /// This is a type-safe wrapper that uses the `Word` trait to determine | ||
| 726 | /// the correct transfer width automatically. | ||
| 727 | /// | ||
| 728 | /// # Arguments | ||
| 729 | /// | ||
| 730 | /// * `edma` - Reference to the eDMA TCD register block | ||
| 731 | /// * `src` - Source buffer | ||
| 732 | /// * `dst` - Destination buffer (must be at least as large as src) | ||
| 733 | /// * `options` - Transfer configuration options | ||
| 734 | /// | ||
| 735 | /// # Safety | ||
| 736 | /// | ||
| 737 | /// The source and destination buffers must remain valid for the | ||
| 738 | /// duration of the transfer. | ||
| 739 | pub unsafe fn transfer_mem_to_mem<W: Word>( | ||
| 740 | &self, | ||
| 741 | src: &[W], | ||
| 742 | dst: &mut [W], | ||
| 743 | options: TransferOptions, | ||
| 744 | ) -> Transfer<'_> { | ||
| 745 | assert!(!src.is_empty()); | 800 | assert!(!src.is_empty()); |
| 746 | assert!(dst.len() >= src.len()); | 801 | assert!(dst.len() >= src.len()); |
| 747 | assert!(src.len() <= 0x7fff); | 802 | assert!(src.len() <= 0x7fff); |
| @@ -752,36 +807,12 @@ impl<C: Channel> DmaChannel<C> { | |||
| 752 | let t = self.tcd(); | 807 | let t = self.tcd(); |
| 753 | 808 | ||
| 754 | // Reset channel state - clear DONE, disable requests, clear errors | 809 | // Reset channel state - clear DONE, disable requests, clear errors |
| 755 | t.ch_csr().write(|w| { | 810 | Self::reset_channel_state(t); |
| 756 | w.erq() | ||
| 757 | .disable() | ||
| 758 | .earq() | ||
| 759 | .disable() | ||
| 760 | .eei() | ||
| 761 | .no_error() | ||
| 762 | .done() | ||
| 763 | .clear_bit_by_one() | ||
| 764 | }); | ||
| 765 | t.ch_es().write(|w| w.err().clear_bit_by_one()); | ||
| 766 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 767 | 811 | ||
| 768 | // Memory barrier to ensure channel state is fully reset before touching TCD | 812 | // Memory barrier to ensure channel state is fully reset before touching TCD |
| 769 | cortex_m::asm::dsb(); | 813 | cortex_m::asm::dsb(); |
| 770 | 814 | ||
| 771 | // Full TCD reset following NXP SDK pattern (EDMA_TcdResetExt). | 815 | Self::clear_tcd(t); |
| 772 | // Reset ALL TCD registers to 0 to clear any stale configuration from | ||
| 773 | // previous transfers. This is critical when reusing a channel. | ||
| 774 | t.tcd_saddr().write(|w| w.saddr().bits(0)); | ||
| 775 | t.tcd_soff().write(|w| w.soff().bits(0)); | ||
| 776 | t.tcd_attr().write(|w| w.bits(0)); | ||
| 777 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(0)); | ||
| 778 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | ||
| 779 | t.tcd_daddr().write(|w| w.daddr().bits(0)); | ||
| 780 | t.tcd_doff().write(|w| w.doff().bits(0)); | ||
| 781 | t.tcd_citer_elinkno().write(|w| w.bits(0)); | ||
| 782 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0)); | ||
| 783 | t.tcd_csr().write(|w| w.bits(0)); // Clear CSR completely | ||
| 784 | t.tcd_biter_elinkno().write(|w| w.bits(0)); | ||
| 785 | 816 | ||
| 786 | // Memory barrier after TCD reset | 817 | // Memory barrier after TCD reset |
| 787 | cortex_m::asm::dsb(); | 818 | cortex_m::asm::dsb(); |
| @@ -792,28 +823,25 @@ impl<C: Channel> DmaChannel<C> { | |||
| 792 | // Now configure the new transfer | 823 | // Now configure the new transfer |
| 793 | 824 | ||
| 794 | // Source address and increment | 825 | // Source address and increment |
| 795 | t.tcd_saddr().write(|w| w.saddr().bits(src.as_ptr() as u32)); | 826 | Self::set_source_ptr(t, src.as_ptr()); |
| 796 | t.tcd_soff().write(|w| w.soff().bits(size.bytes() as u16)); | 827 | Self::set_source_increment(t, size); |
| 797 | 828 | ||
| 798 | // Destination address and increment | 829 | // Destination address and increment |
| 799 | t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); | 830 | Self::set_dest_ptr(t, dst.as_mut_ptr()); |
| 800 | t.tcd_doff().write(|w| w.doff().bits(size.bytes() as u16)); | 831 | Self::set_dest_increment(t, size); |
| 801 | 832 | ||
| 802 | // Transfer attributes (size) | 833 | // Transfer attributes (size) |
| 803 | let hw_size = size.to_hw_size(); | 834 | Self::set_even_transfer_size(t, size); |
| 804 | t.tcd_attr().write(|w| w.ssize().bits(hw_size).dsize().bits(hw_size)); | ||
| 805 | 835 | ||
| 806 | // Minor loop: transfer all bytes in one minor loop | 836 | // Minor loop: transfer all bytes in one minor loop |
| 807 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(byte_count)); | 837 | Self::set_minor_loop_ct_no_offsets(t, byte_count); |
| 808 | 838 | ||
| 809 | // No source/dest adjustment after major loop | 839 | // No source/dest adjustment after major loop |
| 810 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | 840 | Self::set_no_final_adjustments(t); |
| 811 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0)); | ||
| 812 | 841 | ||
| 813 | // Major loop count = 1 (single major loop) | 842 | // Major loop count = 1 (single major loop) |
| 814 | // Write BITER first, then CITER (CITER must match BITER at start) | 843 | // Write BITER first, then CITER (CITER must match BITER at start) |
| 815 | t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); | 844 | Self::set_major_loop_ct_elinkno(t, 1); |
| 816 | t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); | ||
| 817 | 845 | ||
| 818 | // Memory barrier before setting START | 846 | // Memory barrier before setting START |
| 819 | cortex_m::asm::dsb(); | 847 | cortex_m::asm::dsb(); |
| @@ -862,10 +890,7 @@ impl<C: Channel> DmaChannel<C> { | |||
| 862 | /// // buffer is now filled with 0xDEADBEEF | 890 | /// // buffer is now filled with 0xDEADBEEF |
| 863 | /// ``` | 891 | /// ``` |
| 864 | /// | 892 | /// |
| 865 | /// # Safety | 893 | pub fn memset<W: Word>(&self, pattern: &W, dst: &mut [W], options: TransferOptions) -> Transfer<'_> { |
| 866 | /// | ||
| 867 | /// - The pattern and destination buffer must remain valid for the duration of the transfer. | ||
| 868 | pub unsafe fn memset<W: Word>(&self, pattern: &W, dst: &mut [W], options: TransferOptions) -> Transfer<'_> { | ||
| 869 | assert!(!dst.is_empty()); | 894 | assert!(!dst.is_empty()); |
| 870 | assert!(dst.len() <= 0x7fff); | 895 | assert!(dst.len() <= 0x7fff); |
| 871 | 896 | ||
| @@ -877,36 +902,12 @@ impl<C: Channel> DmaChannel<C> { | |||
| 877 | let t = self.tcd(); | 902 | let t = self.tcd(); |
| 878 | 903 | ||
| 879 | // Reset channel state - clear DONE, disable requests, clear errors | 904 | // Reset channel state - clear DONE, disable requests, clear errors |
| 880 | t.ch_csr().write(|w| { | 905 | Self::reset_channel_state(t); |
| 881 | w.erq() | ||
| 882 | .disable() | ||
| 883 | .earq() | ||
| 884 | .disable() | ||
| 885 | .eei() | ||
| 886 | .no_error() | ||
| 887 | .done() | ||
| 888 | .clear_bit_by_one() | ||
| 889 | }); | ||
| 890 | t.ch_es().write(|w| w.err().clear_bit_by_one()); | ||
| 891 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 892 | 906 | ||
| 893 | // Memory barrier to ensure channel state is fully reset before touching TCD | 907 | // Memory barrier to ensure channel state is fully reset before touching TCD |
| 894 | cortex_m::asm::dsb(); | 908 | cortex_m::asm::dsb(); |
| 895 | 909 | ||
| 896 | // Full TCD reset following NXP SDK pattern (EDMA_TcdResetExt). | 910 | Self::clear_tcd(t); |
| 897 | // Reset ALL TCD registers to 0 to clear any stale configuration from | ||
| 898 | // previous transfers. This is critical when reusing a channel. | ||
| 899 | t.tcd_saddr().write(|w| w.saddr().bits(0)); | ||
| 900 | t.tcd_soff().write(|w| w.soff().bits(0)); | ||
| 901 | t.tcd_attr().write(|w| w.bits(0)); | ||
| 902 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(0)); | ||
| 903 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | ||
| 904 | t.tcd_daddr().write(|w| w.daddr().bits(0)); | ||
| 905 | t.tcd_doff().write(|w| w.doff().bits(0)); | ||
| 906 | t.tcd_citer_elinkno().write(|w| w.bits(0)); | ||
| 907 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0)); | ||
| 908 | t.tcd_csr().write(|w| w.bits(0)); // Clear CSR completely | ||
| 909 | t.tcd_biter_elinkno().write(|w| w.bits(0)); | ||
| 910 | 911 | ||
| 911 | // Memory barrier after TCD reset | 912 | // Memory barrier after TCD reset |
| 912 | cortex_m::asm::dsb(); | 913 | cortex_m::asm::dsb(); |
| @@ -923,29 +924,26 @@ impl<C: Channel> DmaChannel<C> { | |||
| 923 | // START triggers. | 924 | // START triggers. |
| 924 | 925 | ||
| 925 | // Source: pattern address, fixed (soff=0) | 926 | // Source: pattern address, fixed (soff=0) |
| 926 | t.tcd_saddr().write(|w| w.saddr().bits(pattern as *const W as u32)); | 927 | Self::set_source_ptr(t, pattern); |
| 927 | t.tcd_soff().write(|w| w.soff().bits(0)); // Fixed source - reads pattern repeatedly | 928 | Self::set_source_fixed(t); |
| 928 | 929 | ||
| 929 | // Destination: memory buffer, incrementing by word size | 930 | // Destination: memory buffer, incrementing by word size |
| 930 | t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); | 931 | Self::set_dest_ptr(t, dst.as_mut_ptr()); |
| 931 | t.tcd_doff().write(|w| w.doff().bits(byte_size as u16)); | 932 | Self::set_dest_increment(t, size); |
| 932 | 933 | ||
| 933 | // Transfer attributes - source and dest are same word size | 934 | // Transfer attributes - source and dest are same word size |
| 934 | let hw_size = size.to_hw_size(); | 935 | Self::set_even_transfer_size(t, size); |
| 935 | t.tcd_attr().write(|w| w.ssize().bits(hw_size).dsize().bits(hw_size)); | ||
| 936 | 936 | ||
| 937 | // Minor loop: transfer ALL bytes in one minor loop (like mem_to_mem) | 937 | // Minor loop: transfer ALL bytes in one minor loop (like mem_to_mem) |
| 938 | // This allows the entire transfer to complete with a single START trigger | 938 | // This allows the entire transfer to complete with a single START trigger |
| 939 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(total_bytes)); | 939 | Self::set_minor_loop_ct_no_offsets(t, total_bytes); |
| 940 | 940 | ||
| 941 | // No address adjustment after major loop | 941 | // No address adjustment after major loop |
| 942 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | 942 | Self::set_no_final_adjustments(t); |
| 943 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0)); | ||
| 944 | 943 | ||
| 945 | // Major loop count = 1 (single major loop, all data in minor loop) | 944 | // Major loop count = 1 (single major loop, all data in minor loop) |
| 946 | // Write BITER first, then CITER (CITER must match BITER at start) | 945 | // Write BITER first, then CITER (CITER must match BITER at start) |
| 947 | t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); | 946 | Self::set_major_loop_ct_elinkno(t, 1); |
| 948 | t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); | ||
| 949 | 947 | ||
| 950 | // Memory barrier before setting START | 948 | // Memory barrier before setting START |
| 951 | cortex_m::asm::dsb(); | 949 | cortex_m::asm::dsb(); |
| @@ -1066,42 +1064,28 @@ impl<C: Channel> DmaChannel<C> { | |||
| 1066 | let t = self.tcd(); | 1064 | let t = self.tcd(); |
| 1067 | 1065 | ||
| 1068 | // Reset channel state | 1066 | // Reset channel state |
| 1069 | t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); | 1067 | Self::reset_channel_state(t); |
| 1070 | t.ch_es().write(|w| w.bits(0)); | ||
| 1071 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 1072 | 1068 | ||
| 1073 | // Addresses | 1069 | // Addresses |
| 1074 | t.tcd_saddr().write(|w| w.saddr().bits(buf.as_ptr() as u32)); | 1070 | Self::set_source_ptr(t, buf.as_ptr()); |
| 1075 | t.tcd_daddr().write(|w| w.daddr().bits(peri_addr as u32)); | 1071 | t.tcd_daddr().write(|w| w.daddr().bits(peri_addr as u32)); |
| 1076 | 1072 | ||
| 1077 | // Offsets: Source increments, Dest fixed | 1073 | // Offsets: Source increments, Dest fixed |
| 1078 | t.tcd_soff().write(|w| w.soff().bits(byte_size as u16)); | 1074 | Self::set_source_increment(t, size); |
| 1079 | t.tcd_doff().write(|w| w.doff().bits(0)); | 1075 | Self::set_dest_fixed(t); |
| 1080 | 1076 | ||
| 1081 | // Attributes: set size and explicitly disable modulo | 1077 | // Attributes: set size and explicitly disable modulo |
| 1082 | let hw_size = size.to_hw_size(); | 1078 | Self::set_even_transfer_size(t, size); |
| 1083 | t.tcd_attr().write(|w| { | ||
| 1084 | w.ssize() | ||
| 1085 | .bits(hw_size) | ||
| 1086 | .dsize() | ||
| 1087 | .bits(hw_size) | ||
| 1088 | .smod() | ||
| 1089 | .disable() | ||
| 1090 | .dmod() | ||
| 1091 | .bits(0) | ||
| 1092 | }); | ||
| 1093 | 1079 | ||
| 1094 | // Minor loop: transfer one word per request (match old: only set nbytes) | 1080 | // Minor loop: transfer one word per request (match old: only set nbytes) |
| 1095 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(byte_size as u32)); | 1081 | Self::set_minor_loop_ct_no_offsets(t, byte_size as u32); |
| 1096 | 1082 | ||
| 1097 | // No final adjustments | 1083 | // No final adjustments |
| 1098 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | 1084 | Self::set_no_final_adjustments(t); |
| 1099 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0)); | ||
| 1100 | 1085 | ||
| 1101 | // Major loop count = number of words | 1086 | // Major loop count = number of words |
| 1102 | let count = buf.len() as u16; | 1087 | let count = buf.len() as u16; |
| 1103 | t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); | 1088 | Self::set_major_loop_ct_elinkno(t, count); |
| 1104 | t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable()); | ||
| 1105 | 1089 | ||
| 1106 | // CSR: interrupt on major loop complete and auto-clear ERQ | 1090 | // CSR: interrupt on major loop complete and auto-clear ERQ |
| 1107 | t.tcd_csr().write(|w| { | 1091 | t.tcd_csr().write(|w| { |
| @@ -1232,60 +1216,28 @@ impl<C: Channel> DmaChannel<C> { | |||
| 1232 | let t = self.tcd(); | 1216 | let t = self.tcd(); |
| 1233 | 1217 | ||
| 1234 | // Reset channel control/error/interrupt state | 1218 | // Reset channel control/error/interrupt state |
| 1235 | t.ch_csr().write(|w| { | 1219 | Self::reset_channel_state(t); |
| 1236 | w.erq() | ||
| 1237 | .disable() | ||
| 1238 | .earq() | ||
| 1239 | .disable() | ||
| 1240 | .eei() | ||
| 1241 | .no_error() | ||
| 1242 | .ebw() | ||
| 1243 | .disable() | ||
| 1244 | .done() | ||
| 1245 | .clear_bit_by_one() | ||
| 1246 | }); | ||
| 1247 | t.ch_es().write(|w| w.bits(0)); | ||
| 1248 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 1249 | 1220 | ||
| 1250 | // Source: peripheral register, fixed | 1221 | // Source: peripheral register, fixed |
| 1251 | t.tcd_saddr().write(|w| w.saddr().bits(peri_addr as u32)); | 1222 | Self::set_source_ptr(t, peri_addr); |
| 1252 | t.tcd_soff().write(|w| w.soff().bits(0)); // No increment | 1223 | Self::set_source_fixed(t); |
| 1253 | 1224 | ||
| 1254 | // Destination: memory buffer, incrementing | 1225 | // Destination: memory buffer, incrementing |
| 1255 | t.tcd_daddr().write(|w| w.daddr().bits(buf.as_mut_ptr() as u32)); | 1226 | Self::set_dest_ptr(t, buf.as_mut_ptr()); |
| 1256 | t.tcd_doff().write(|w| w.doff().bits(byte_size as u16)); | 1227 | Self::set_dest_increment(t, size); |
| 1257 | 1228 | ||
| 1258 | // Transfer attributes: set size and explicitly disable modulo | 1229 | // Transfer attributes: set size and explicitly disable modulo |
| 1259 | let hw_size = size.to_hw_size(); | 1230 | Self::set_even_transfer_size(t, size); |
| 1260 | t.tcd_attr().write(|w| { | ||
| 1261 | w.ssize() | ||
| 1262 | .bits(hw_size) | ||
| 1263 | .dsize() | ||
| 1264 | .bits(hw_size) | ||
| 1265 | .smod() | ||
| 1266 | .disable() | ||
| 1267 | .dmod() | ||
| 1268 | .bits(0) | ||
| 1269 | }); | ||
| 1270 | 1231 | ||
| 1271 | // Minor loop: transfer one word per request, no offsets | 1232 | // Minor loop: transfer one word per request, no offsets |
| 1272 | t.tcd_nbytes_mloffno().write(|w| { | 1233 | Self::set_minor_loop_ct_no_offsets(t, byte_size as u32); |
| 1273 | w.nbytes() | ||
| 1274 | .bits(byte_size as u32) | ||
| 1275 | .dmloe() | ||
| 1276 | .offset_not_applied() | ||
| 1277 | .smloe() | ||
| 1278 | .offset_not_applied() | ||
| 1279 | }); | ||
| 1280 | 1234 | ||
| 1281 | // Major loop count = number of words | 1235 | // Major loop count = number of words |
| 1282 | let count = buf.len() as u16; | 1236 | let count = buf.len() as u16; |
| 1283 | t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); | 1237 | Self::set_major_loop_ct_elinkno(t, count); |
| 1284 | t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable()); | ||
| 1285 | 1238 | ||
| 1286 | // No address adjustment after major loop | 1239 | // No address adjustment after major loop |
| 1287 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | 1240 | Self::set_no_final_adjustments(t); |
| 1288 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0)); | ||
| 1289 | 1241 | ||
| 1290 | // Control/status: interrupt on major complete, auto-clear ERQ when done | 1242 | // Control/status: interrupt on major complete, auto-clear ERQ when done |
| 1291 | t.tcd_csr().write(|w| { | 1243 | t.tcd_csr().write(|w| { |
| @@ -1356,42 +1308,28 @@ impl<C: Channel> DmaChannel<C> { | |||
| 1356 | let t = self.tcd(); | 1308 | let t = self.tcd(); |
| 1357 | 1309 | ||
| 1358 | // Reset channel state | 1310 | // Reset channel state |
| 1359 | t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); | 1311 | Self::reset_channel_state(t); |
| 1360 | t.ch_es().write(|w| w.bits(0)); | ||
| 1361 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 1362 | 1312 | ||
| 1363 | // Addresses | 1313 | // Addresses |
| 1364 | t.tcd_saddr().write(|w| w.saddr().bits(buf.as_ptr() as u32)); | 1314 | Self::set_source_ptr(t, buf.as_ptr()); |
| 1365 | t.tcd_daddr().write(|w| w.daddr().bits(peri_addr as u32)); | 1315 | Self::set_dest_ptr(t, peri_addr); |
| 1366 | 1316 | ||
| 1367 | // Offsets: Source increments, Dest fixed | 1317 | // Offsets: Source increments, Dest fixed |
| 1368 | t.tcd_soff().write(|w| w.soff().bits(byte_size as u16)); | 1318 | Self::set_source_increment(t, size); |
| 1369 | t.tcd_doff().write(|w| w.doff().bits(0)); | 1319 | Self::set_dest_fixed(t); |
| 1370 | 1320 | ||
| 1371 | // Attributes: set size and explicitly disable modulo | 1321 | // Attributes: set size and explicitly disable modulo |
| 1372 | let hw_size = size.to_hw_size(); | 1322 | Self::set_even_transfer_size(t, size); |
| 1373 | t.tcd_attr().write(|w| { | ||
| 1374 | w.ssize() | ||
| 1375 | .bits(hw_size) | ||
| 1376 | .dsize() | ||
| 1377 | .bits(hw_size) | ||
| 1378 | .smod() | ||
| 1379 | .disable() | ||
| 1380 | .dmod() | ||
| 1381 | .bits(0) | ||
| 1382 | }); | ||
| 1383 | 1323 | ||
| 1384 | // Minor loop: transfer one word per request | 1324 | // Minor loop: transfer one word per request |
| 1385 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(byte_size as u32)); | 1325 | Self::set_minor_loop_ct_no_offsets(t, byte_size as u32); |
| 1386 | 1326 | ||
| 1387 | // No final adjustments | 1327 | // No final adjustments |
| 1388 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | 1328 | Self::set_no_final_adjustments(t); |
| 1389 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0)); | ||
| 1390 | 1329 | ||
| 1391 | // Major loop count = number of words | 1330 | // Major loop count = number of words |
| 1392 | let count = buf.len() as u16; | 1331 | let count = buf.len() as u16; |
| 1393 | t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); | 1332 | Self::set_major_loop_ct_elinkno(t, count); |
| 1394 | t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable()); | ||
| 1395 | 1333 | ||
| 1396 | // CSR: optional interrupt on major loop complete and auto-clear ERQ | 1334 | // CSR: optional interrupt on major loop complete and auto-clear ERQ |
| 1397 | t.tcd_csr().write(|w| { | 1335 | t.tcd_csr().write(|w| { |
| @@ -1456,53 +1394,28 @@ impl<C: Channel> DmaChannel<C> { | |||
| 1456 | let t = self.tcd(); | 1394 | let t = self.tcd(); |
| 1457 | 1395 | ||
| 1458 | // Reset channel control/error/interrupt state | 1396 | // Reset channel control/error/interrupt state |
| 1459 | t.ch_csr().write(|w| { | 1397 | Self::reset_channel_state(t); |
| 1460 | w.erq() | ||
| 1461 | .disable() | ||
| 1462 | .earq() | ||
| 1463 | .disable() | ||
| 1464 | .eei() | ||
| 1465 | .no_error() | ||
| 1466 | .ebw() | ||
| 1467 | .disable() | ||
| 1468 | .done() | ||
| 1469 | .clear_bit_by_one() | ||
| 1470 | }); | ||
| 1471 | t.ch_es().write(|w| w.bits(0)); | ||
| 1472 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 1473 | 1398 | ||
| 1474 | // Source: peripheral register, fixed | 1399 | // Source: peripheral register, fixed |
| 1475 | t.tcd_saddr().write(|w| w.saddr().bits(peri_addr as u32)); | 1400 | Self::set_source_ptr(t, peri_addr); |
| 1476 | t.tcd_soff().write(|w| w.soff().bits(0)); | 1401 | Self::set_source_fixed(t); |
| 1477 | 1402 | ||
| 1478 | // Destination: memory buffer, incrementing | 1403 | // Destination: memory buffer, incrementing |
| 1479 | t.tcd_daddr().write(|w| w.daddr().bits(buf.as_mut_ptr() as u32)); | 1404 | Self::set_dest_ptr(t, buf.as_mut_ptr()); |
| 1480 | t.tcd_doff().write(|w| w.doff().bits(byte_size as u16)); | 1405 | Self::set_dest_increment(t, size); |
| 1481 | 1406 | ||
| 1482 | // Attributes: set size and explicitly disable modulo | 1407 | // Attributes: set size and explicitly disable modulo |
| 1483 | let hw_size = size.to_hw_size(); | 1408 | Self::set_even_transfer_size(t, size); |
| 1484 | t.tcd_attr().write(|w| { | ||
| 1485 | w.ssize() | ||
| 1486 | .bits(hw_size) | ||
| 1487 | .dsize() | ||
| 1488 | .bits(hw_size) | ||
| 1489 | .smod() | ||
| 1490 | .disable() | ||
| 1491 | .dmod() | ||
| 1492 | .bits(0) | ||
| 1493 | }); | ||
| 1494 | 1409 | ||
| 1495 | // Minor loop: transfer one word per request | 1410 | // Minor loop: transfer one word per request |
| 1496 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(byte_size as u32)); | 1411 | Self::set_minor_loop_ct_no_offsets(t, byte_size as u32); |
| 1497 | 1412 | ||
| 1498 | // No final adjustments | 1413 | // No final adjustments |
| 1499 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | 1414 | Self::set_no_final_adjustments(t); |
| 1500 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(0)); | ||
| 1501 | 1415 | ||
| 1502 | // Major loop count = number of words | 1416 | // Major loop count = number of words |
| 1503 | let count = buf.len() as u16; | 1417 | let count = buf.len() as u16; |
| 1504 | t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); | 1418 | Self::set_major_loop_ct_elinkno(t, count); |
| 1505 | t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable()); | ||
| 1506 | 1419 | ||
| 1507 | // CSR: optional interrupt on major loop complete and auto-clear ERQ | 1420 | // CSR: optional interrupt on major loop complete and auto-clear ERQ |
| 1508 | t.tcd_csr().write(|w| { | 1421 | t.tcd_csr().write(|w| { |
| @@ -2233,56 +2146,25 @@ impl<C: Channel> DmaChannel<C> { | |||
| 2233 | let t = self.tcd(); | 2146 | let t = self.tcd(); |
| 2234 | 2147 | ||
| 2235 | // Reset channel state | 2148 | // Reset channel state |
| 2236 | t.ch_csr().write(|w| { | 2149 | Self::reset_channel_state(t); |
| 2237 | w.erq() | ||
| 2238 | .disable() | ||
| 2239 | .earq() | ||
| 2240 | .disable() | ||
| 2241 | .eei() | ||
| 2242 | .no_error() | ||
| 2243 | .ebw() | ||
| 2244 | .disable() | ||
| 2245 | .done() | ||
| 2246 | .clear_bit_by_one() | ||
| 2247 | }); | ||
| 2248 | t.ch_es().write(|w| w.bits(0)); | ||
| 2249 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 2250 | 2150 | ||
| 2251 | // Source: peripheral register, fixed | 2151 | // Source: peripheral register, fixed |
| 2252 | t.tcd_saddr().write(|w| w.saddr().bits(peri_addr as u32)); | 2152 | Self::set_source_ptr(t, peri_addr); |
| 2253 | t.tcd_soff().write(|w| w.soff().bits(0)); // No increment | 2153 | Self::set_source_fixed(t); |
| 2254 | 2154 | ||
| 2255 | // Destination: memory buffer, incrementing | 2155 | // Destination: memory buffer, incrementing |
| 2256 | t.tcd_daddr().write(|w| w.daddr().bits(buf.as_mut_ptr() as u32)); | 2156 | Self::set_dest_ptr(t, buf.as_mut_ptr()); |
| 2257 | t.tcd_doff().write(|w| w.doff().bits(byte_size as u16)); | 2157 | Self::set_dest_increment(t, size); |
| 2258 | 2158 | ||
| 2259 | // Transfer attributes | 2159 | // Transfer attributes |
| 2260 | let hw_size = size.to_hw_size(); | 2160 | Self::set_even_transfer_size(t, size); |
| 2261 | t.tcd_attr().write(|w| { | ||
| 2262 | w.ssize() | ||
| 2263 | .bits(hw_size) | ||
| 2264 | .dsize() | ||
| 2265 | .bits(hw_size) | ||
| 2266 | .smod() | ||
| 2267 | .disable() | ||
| 2268 | .dmod() | ||
| 2269 | .bits(0) | ||
| 2270 | }); | ||
| 2271 | 2161 | ||
| 2272 | // Minor loop: transfer one word per request | 2162 | // Minor loop: transfer one word per request |
| 2273 | t.tcd_nbytes_mloffno().write(|w| { | 2163 | Self::set_minor_loop_ct_no_offsets(t, byte_size as u32); |
| 2274 | w.nbytes() | ||
| 2275 | .bits(byte_size as u32) | ||
| 2276 | .dmloe() | ||
| 2277 | .offset_not_applied() | ||
| 2278 | .smloe() | ||
| 2279 | .offset_not_applied() | ||
| 2280 | }); | ||
| 2281 | 2164 | ||
| 2282 | // Major loop count = buffer size | 2165 | // Major loop count = buffer size |
| 2283 | let count = buf.len() as u16; | 2166 | let count = buf.len() as u16; |
| 2284 | t.tcd_citer_elinkno().write(|w| w.citer().bits(count).elink().disable()); | 2167 | Self::set_major_loop_ct_elinkno(t, count); |
| 2285 | t.tcd_biter_elinkno().write(|w| w.biter().bits(count).elink().disable()); | ||
| 2286 | 2168 | ||
| 2287 | // After major loop: reset destination to buffer start (circular) | 2169 | // After major loop: reset destination to buffer start (circular) |
| 2288 | let buf_bytes = (buf.len() * byte_size) as i32; | 2170 | let buf_bytes = (buf.len() * byte_size) as i32; |
| @@ -2475,18 +2357,7 @@ impl<W: Word> ScatterGatherBuilder<W> { | |||
| 2475 | 2357 | ||
| 2476 | // Reset channel state - clear DONE, disable requests, clear errors | 2358 | // Reset channel state - clear DONE, disable requests, clear errors |
| 2477 | // This ensures the channel is in a clean state before loading the TCD | 2359 | // This ensures the channel is in a clean state before loading the TCD |
| 2478 | t.ch_csr().write(|w| { | 2360 | Self::reset_channel_state(t); |
| 2479 | w.erq() | ||
| 2480 | .disable() | ||
| 2481 | .earq() | ||
| 2482 | .disable() | ||
| 2483 | .eei() | ||
| 2484 | .no_error() | ||
| 2485 | .done() | ||
| 2486 | .clear_bit_by_one() | ||
| 2487 | }); | ||
| 2488 | t.ch_es().write(|w| w.err().clear_bit_by_one()); | ||
| 2489 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 2490 | 2361 | ||
| 2491 | // Memory barrier to ensure channel state is reset before loading TCD | 2362 | // Memory barrier to ensure channel state is reset before loading TCD |
| 2492 | cortex_m::asm::dsb(); | 2363 | cortex_m::asm::dsb(); |
