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_dma_mcuxpresso/dma.h" 25 #include "pw_status/status.h" 26 #include "pw_sync/interrupt_spin_lock.h" 27 #include "pw_sync/timed_thread_notification.h" 28 #include "pw_uart/uart.h" 29 30 namespace pw::uart { 31 32 class DmaUartMcuxpresso final : public Uart { 33 public: 34 // Configuration structure 35 struct Config { 36 USART_Type* usart_base; // Base of USART control struct 37 uint32_t baud_rate; // Desired communication speed 38 bool flow_control = false; // Hardware flow control setting 39 usart_parity_mode_t parity = kUSART_ParityDisabled; // Parity setting 40 usart_stop_bit_count_t stop_bits = 41 kUSART_OneStopBit; // Number of stop bits to use 42 dma::McuxpressoDmaChannel& rx_dma_ch; // Receive DMA channel 43 dma::McuxpressoDmaChannel& tx_dma_ch; // Transmit DMA channel 44 inputmux_signal_t rx_input_mux_dmac_ch_request_en; // Rx input mux signal 45 inputmux_signal_t tx_input_mux_dmac_ch_request_en; // Tx input mux signal 46 ByteSpan buffer; // Receive ring buffer 47 pw::clock_tree::ClockTree* clock_tree{}; // Optional clock Tree 48 pw::clock_tree::Element* 49 clock_tree_element{}; // Optional clock tree element 50 }; 51 DmaUartMcuxpresso(const Config & config)52 DmaUartMcuxpresso(const Config& config) 53 : rx_data_{.ring_buffer = config.buffer}, 54 config_(config), 55 clock_tree_element_controller_(config.clock_tree, 56 config.clock_tree_element) {} 57 58 ~DmaUartMcuxpresso(); 59 60 DmaUartMcuxpresso(const DmaUartMcuxpresso& other) = delete; 61 62 DmaUartMcuxpresso& operator=(const DmaUartMcuxpresso& other) = delete; 63 64 private: 65 // Usart DMA TX data structure 66 struct UsartDmaTxData { 67 ConstByteSpan buffer; // TX transaction buffer 68 size_t tx_idx; // Position within TX transaction 69 usart_transfer_t transfer; // USART TX transfer structure 70 std::atomic_uint8_t busy; // Flag to prevent concurrent access to TX queue 71 pw::sync::TimedThreadNotification 72 notification; // TX completion notification 73 }; 74 75 // Usart DMA RX data structure 76 struct UsartDmaRxData { 77 ByteSpan ring_buffer; // Receive ring buffer 78 size_t ring_buffer_read_idx{}; // ring buffer reader index 79 size_t ring_buffer_write_idx{}; // ring buffer writer index 80 size_t data_received{}; // data received and acknowledged by completion 81 // callback 82 size_t data_copied{}; // data copied out to receiver 83 // completion callback will be executed when completion size decreases to 0 84 // bytes 85 size_t completion_size{}; 86 usart_transfer_t transfer{}; // USART RX transfer structure 87 std::atomic_uint8_t 88 busy{}; // Flag to prevent concurrent access to RX ring buffer 89 pw::sync::TimedThreadNotification 90 notification{}; // RX completion notification 91 }; 92 93 // Since we are calling USART_TransferGetReceiveCountDMA we may only 94 // transfer DMA_MAX_TRANSFER_COUNT - 1 bytes per DMA transfer. 95 static constexpr size_t kUsartDmaMaxTransferCount = 96 DMA_MAX_TRANSFER_COUNT - 1; 97 98 // A reader may at most wait for 25% of the ring buffer size before data 99 // needs to be copied out to the caller. 100 static constexpr size_t kUsartRxRingBufferSplitCount = 4; 101 102 Status DoEnable(bool enable) override; 103 Status DoSetBaudRate(uint32_t baud_rate) override; 104 Status DoSetFlowControl(bool enable) override; 105 StatusWithSize DoTryReadFor( 106 ByteSpan rx_buffer, 107 size_t min_bytes, 108 std::optional<chrono::SystemClock::duration> timeout) override; 109 StatusWithSize DoTryWriteFor( 110 ConstByteSpan tx_buffer, 111 std::optional<chrono::SystemClock::duration> timeout) override; 112 size_t DoConservativeReadAvailable() override; 113 Status DoClearPendingReceiveBytes() override; 114 Status DoFlushOutput() override; 115 116 // Helper functions 117 static IRQn_Type GetInterrupt(const DMA_Type* base); 118 Status Init(); 119 void Deinit(); 120 void TriggerReadDma() PW_EXCLUSIVE_LOCKS_REQUIRED(interrupt_lock_); 121 void TriggerWriteDma(); 122 123 StatusWithSize TransferGetReceiveDMACount() 124 PW_LOCKS_EXCLUDED(interrupt_lock_); 125 StatusWithSize TransferGetReceiveDMACountLockHeld() 126 PW_EXCLUSIVE_LOCKS_REQUIRED(interrupt_lock_); 127 size_t GetReceiveTransferRemainingBytes(); 128 static void TxRxCompletionCallback(USART_Type* base, 129 usart_dma_handle_t* state, 130 status_t status, 131 void* param); 132 Status WaitForReceiveBytes( 133 size_t bytes_needed, 134 std::optional<chrono::SystemClock::time_point> deadline); 135 void CopyReceiveData(ByteBuilder& bb, size_t copy_size); 136 137 pw::sync::InterruptSpinLock 138 interrupt_lock_; // Lock to synchronize with interrupt handler and to 139 // guarantee exclusive access to DMA control registers 140 usart_dma_handle_t uart_dma_handle_; // USART DMA Handle 141 struct UsartDmaTxData tx_data_; // TX data 142 struct UsartDmaRxData rx_data_; // RX data 143 144 Config config_; // USART DMA configuration 145 pw::clock_tree::ElementController 146 clock_tree_element_controller_; // Element controller encapsulating 147 // optional clock tree information 148 bool 149 initialized_; // Whether the USART and DMA channels have been initialized 150 uint32_t flexcomm_clock_freq_{}; 151 }; 152 153 } // namespace pw::uart 154