xref: /aosp_15_r20/external/pigweed/pw_uart_mcuxpresso/public/pw_uart_mcuxpresso/dma_uart.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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