1 // Copyright 2024 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <atomic> 17 18 #include "fsl_dma.h" 19 #include "fsl_inputmux.h" 20 #include "fsl_usart_dma.h" 21 #include "pw_bytes/byte_builder.h" 22 #include "pw_bytes/span.h" 23 #include "pw_clock_tree/clock_tree.h" 24 #include "pw_status/status.h" 25 #include "pw_stream/stream.h" 26 #include "pw_sync/interrupt_spin_lock.h" 27 #include "pw_sync/thread_notification.h" 28 29 namespace pw::stream { 30 31 class UartDmaStreamMcuxpresso final : public NonSeekableReaderWriter { 32 public: 33 // Configuration structure 34 struct Config { 35 USART_Type* usart_base; // Base of USART control struct 36 uint32_t baud_rate; // Desired communication speed 37 usart_parity_mode_t parity; // Parity setting 38 usart_stop_bit_count_t stop_bits; // Number of stop bits to use 39 DMA_Type* dma_base; // Base of DMA control struct 40 uint32_t rx_dma_ch; // Receive DMA channel 41 uint32_t tx_dma_ch; // Transmit DMA channel 42 inputmux_signal_t rx_input_mux_dmac_ch_request_en; // Rx input mux signal 43 inputmux_signal_t tx_input_mux_dmac_ch_request_en; // Tx input mux signal 44 ByteSpan buffer; // Receive ring buffer 45 pw::clock_tree::ClockTree* clock_tree{}; // Optional clock Tree 46 pw::clock_tree::Element* 47 clock_tree_element{}; // Optional clock tree element 48 }; 49 UartDmaStreamMcuxpresso(const Config & config)50 UartDmaStreamMcuxpresso(const Config& config) 51 : rx_data_{.ring_buffer = config.buffer}, 52 config_(config), 53 clock_tree_element_controller_(config.clock_tree, 54 config.clock_tree_element) {} 55 56 ~UartDmaStreamMcuxpresso(); 57 58 UartDmaStreamMcuxpresso(const UartDmaStreamMcuxpresso& other) = delete; 59 60 UartDmaStreamMcuxpresso& operator=(const UartDmaStreamMcuxpresso& other) = 61 delete; 62 63 pw::Status Init(uint32_t srcclk); 64 65 private: 66 // Usart DMA TX data structure 67 struct UsartDmaTxData { 68 ConstByteSpan buffer; // TX transaction buffer 69 size_t tx_idx; // Position within TX transaction 70 dma_handle_t dma_handle; // DMA handle 71 usart_transfer_t transfer; // USART TX transfer structure 72 std::atomic_uint8_t busy; // Flag to prevent concurrent access to TX queue 73 pw::sync::ThreadNotification notification; // TX completion notification 74 }; 75 76 // Usart DMA RX data structure 77 struct UsartDmaRxData { 78 ByteSpan ring_buffer; // Receive ring buffer 79 size_t ring_buffer_read_idx{}; // ring buffer reader index 80 size_t ring_buffer_write_idx{}; // ring buffer writer index 81 size_t data_received{}; // data received and acknowledged by completion 82 // callback 83 size_t data_copied{}; // data copied out to receiver 84 // completion callback will be executed when completion size decreases to 0 85 // bytes 86 size_t completion_size{}; 87 usart_transfer_t transfer{}; // USART RX transfer structure 88 dma_handle_t dma_handle{}; // DMA handle 89 std::atomic_uint8_t 90 busy{}; // Flag to prevent concurrent access to RX ring buffer 91 pw::sync::ThreadNotification notification{}; // RX completion notification 92 }; 93 94 // Since we are calling USART_TransferGetReceiveCountDMA we may only 95 // transfer DMA_MAX_TRANSFER_COUNT - 1 bytes per DMA transfer. 96 static constexpr size_t kUsartDmaMaxTransferCount = 97 DMA_MAX_TRANSFER_COUNT - 1; 98 99 // A reader may at most wait for 25% of the ring buffer size before data 100 // needs to be copied out to the caller. 101 static constexpr size_t kUsartRxRingBufferSplitCount = 4; 102 103 StatusWithSize DoRead(ByteSpan) override; 104 Status DoWrite(ConstByteSpan) override; 105 106 // Helper functions 107 static IRQn_Type GetInterrupt(const DMA_Type* base); 108 void Deinit(); 109 void TriggerReadDma() PW_EXCLUSIVE_LOCKS_REQUIRED(interrupt_lock_); 110 void TriggerWriteDma(); 111 112 StatusWithSize TransferGetReceiveDMACount() 113 PW_LOCKS_EXCLUDED(interrupt_lock_); 114 StatusWithSize TransferGetReceiveDMACountLockHeld() 115 PW_EXCLUSIVE_LOCKS_REQUIRED(interrupt_lock_); 116 size_t GetReceiveTransferRemainingBytes(); 117 static void TxRxCompletionCallback(USART_Type* base, 118 usart_dma_handle_t* state, 119 status_t status, 120 void* param); 121 Status WaitForReceiveBytes(size_t bytes_needed); 122 void CopyReceiveData(ByteBuilder& bb, size_t copy_size); 123 124 pw::sync::InterruptSpinLock 125 interrupt_lock_; // Lock to synchronize with interrupt handler and to 126 // guarantee exclusive access to DMA control registers 127 usart_dma_handle_t uart_dma_handle_; // USART DMA Handle 128 struct UsartDmaTxData tx_data_; // TX data 129 struct UsartDmaRxData rx_data_; // RX data 130 131 Config const config_; // USART DMA configuration 132 pw::clock_tree::ElementController 133 clock_tree_element_controller_; // Element controller encapsulating 134 // optional clock tree information 135 bool 136 initialized_; // Whether the USART and DMA channels have been initialized 137 }; 138 139 } // namespace pw::stream 140