1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2020 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_uart_dma.h"
10 
11 /*******************************************************************************
12  * Definitions
13  ******************************************************************************/
14 
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.uart_dma"
18 #endif
19 
20 /* Array of UART handle. */
21 #if (defined(UART5))
22 #define UART_HANDLE_ARRAY_SIZE 6
23 #else /* UART5 */
24 #if (defined(UART4))
25 #define UART_HANDLE_ARRAY_SIZE 5
26 #else /* UART4 */
27 #if (defined(UART3))
28 #define UART_HANDLE_ARRAY_SIZE 4
29 #else /* UART3 */
30 #if (defined(UART2))
31 #define UART_HANDLE_ARRAY_SIZE 3
32 #else /* UART2 */
33 #if (defined(UART1))
34 #define UART_HANDLE_ARRAY_SIZE 2
35 #else /* UART1 */
36 #if (defined(UART0))
37 #define UART_HANDLE_ARRAY_SIZE 1
38 #else /* UART0 */
39 #error No UART instance.
40 #endif /* UART 0 */
41 #endif /* UART 1 */
42 #endif /* UART 2 */
43 #endif /* UART 3 */
44 #endif /* UART 4 */
45 #endif /* UART 5 */
46 
47 /*<! Structure definition for uart_dma_handle_t. The structure is private. */
48 typedef struct _uart_dma_private_handle
49 {
50     UART_Type *base;
51     uart_dma_handle_t *handle;
52 } uart_dma_private_handle_t;
53 
54 /* UART DMA transfer handle. */
55 enum
56 {
57     kUART_TxIdle, /* TX idle. */
58     kUART_TxBusy, /* TX busy. */
59     kUART_RxIdle, /* RX idle. */
60     kUART_RxBusy  /* RX busy. */
61 };
62 
63 /*******************************************************************************
64  * Variables
65  ******************************************************************************/
66 
67 /*<! Private handle only used for internally. */
68 static uart_dma_private_handle_t s_dmaPrivateHandle[UART_HANDLE_ARRAY_SIZE];
69 
70 /*******************************************************************************
71  * Prototypes
72  ******************************************************************************/
73 
74 /*!
75  * @brief UART DMA send finished callback function.
76  *
77  * This function is called when UART DMA send finished. It disables the UART
78  * TX DMA request and sends @ref kStatus_UART_TxIdle to UART callback.
79  *
80  * @param handle The DMA handle.
81  * @param param Callback function parameter.
82  */
83 static void UART_TransferSendDMACallback(dma_handle_t *handle, void *param);
84 
85 /*!
86  * @brief UART DMA receive finished callback function.
87  *
88  * This function is called when UART DMA receive finished. It disables the UART
89  * RX DMA request and sends @ref kStatus_UART_RxIdle to UART callback.
90  *
91  * @param handle The DMA handle.
92  * @param param Callback function parameter.
93  */
94 static void UART_TransferReceiveDMACallback(dma_handle_t *handle, void *param);
95 
96 /*******************************************************************************
97  * Code
98  ******************************************************************************/
99 
UART_TransferSendDMACallback(dma_handle_t * handle,void * param)100 static void UART_TransferSendDMACallback(dma_handle_t *handle, void *param)
101 {
102     assert(handle != NULL);
103     assert(param != NULL);
104 
105     uart_dma_private_handle_t *uartPrivateHandle = (uart_dma_private_handle_t *)param;
106 
107     /* Disable UART TX DMA. */
108     UART_EnableTxDMA(uartPrivateHandle->base, false);
109 
110     /* Disable interrupt. */
111     DMA_DisableInterrupts(handle->base, handle->channel);
112 
113     /* Enable tx complete interrupt */
114     UART_EnableInterrupts(uartPrivateHandle->base, (uint32_t)kUART_TransmissionCompleteInterruptEnable);
115 }
116 
UART_TransferReceiveDMACallback(dma_handle_t * handle,void * param)117 static void UART_TransferReceiveDMACallback(dma_handle_t *handle, void *param)
118 {
119     assert(handle != NULL);
120     assert(param != NULL);
121 
122     uart_dma_private_handle_t *uartPrivateHandle = (uart_dma_private_handle_t *)param;
123 
124     /* Disable UART RX DMA. */
125     UART_EnableRxDMA(uartPrivateHandle->base, false);
126 
127     /* Disable interrupt. */
128     DMA_DisableInterrupts(handle->base, handle->channel);
129 
130     uartPrivateHandle->handle->rxState = (uint8_t)kUART_RxIdle;
131 
132     if (uartPrivateHandle->handle->callback != NULL)
133     {
134         uartPrivateHandle->handle->callback(uartPrivateHandle->base, uartPrivateHandle->handle, kStatus_UART_RxIdle,
135                                             uartPrivateHandle->handle->userData);
136     }
137 }
138 
139 /*!
140  * brief Initializes the UART handle which is used in transactional functions and sets the callback.
141  *
142  * param base UART peripheral base address.
143  * param handle Pointer to the uart_dma_handle_t structure.
144  * param callback UART callback, NULL means no callback.
145  * param userData User callback function data.
146  * param rxDmaHandle User requested DMA handle for the RX DMA transfer.
147  * param txDmaHandle User requested DMA handle for the TX DMA transfer.
148  */
UART_TransferCreateHandleDMA(UART_Type * base,uart_dma_handle_t * handle,uart_dma_transfer_callback_t callback,void * userData,dma_handle_t * txDmaHandle,dma_handle_t * rxDmaHandle)149 void UART_TransferCreateHandleDMA(UART_Type *base,
150                                   uart_dma_handle_t *handle,
151                                   uart_dma_transfer_callback_t callback,
152                                   void *userData,
153                                   dma_handle_t *txDmaHandle,
154                                   dma_handle_t *rxDmaHandle)
155 {
156     assert(handle != NULL);
157 
158     uint32_t instance = UART_GetInstance(base);
159 
160     (void)memset(handle, 0, sizeof(*handle));
161 
162     s_dmaPrivateHandle[instance].base   = base;
163     s_dmaPrivateHandle[instance].handle = handle;
164 
165     handle->rxState = (uint8_t)kUART_RxIdle;
166     handle->txState = (uint8_t)kUART_TxIdle;
167 
168     handle->callback = callback;
169     handle->userData = userData;
170 
171 #if defined(FSL_FEATURE_UART_HAS_FIFO) && FSL_FEATURE_UART_HAS_FIFO
172     /* Note:
173        Take care of the RX FIFO, DMA request only assert when received bytes
174        equal or more than RX water mark, there is potential issue if RX water
175        mark larger than 1.
176        For example, if RX FIFO water mark is 2, upper layer needs 5 bytes and
177        5 bytes are received. the last byte will be saved in FIFO but not trigger
178        DMA transfer because the water mark is 2.
179      */
180     if (rxDmaHandle != NULL)
181     {
182         base->RWFIFO = 1U;
183     }
184 #endif
185 
186     handle->rxDmaHandle = rxDmaHandle;
187     handle->txDmaHandle = txDmaHandle;
188 
189     /* Save the handle in global variables to support the double weak mechanism. */
190     s_uartHandle[instance] = handle;
191     /* Save IRQ handler into static ISR function pointer. */
192     s_uartIsr = UART_TransferDMAHandleIRQ;
193     /* Disable internal IRQs and enable global IRQ. */
194     UART_DisableInterrupts(base, (uint32_t)kUART_AllInterruptsEnable);
195     (void)EnableIRQ(s_uartIRQ[instance]);
196 
197     /* Configure TX. */
198     if (txDmaHandle != NULL)
199     {
200         DMA_SetCallback(txDmaHandle, UART_TransferSendDMACallback, &s_dmaPrivateHandle[instance]);
201     }
202 
203     /* Configure RX. */
204     if (rxDmaHandle != NULL)
205     {
206         DMA_SetCallback(rxDmaHandle, UART_TransferReceiveDMACallback, &s_dmaPrivateHandle[instance]);
207     }
208 }
209 
210 /*!
211  * brief Sends data using DMA.
212  *
213  * This function sends data using DMA. This is non-blocking function, which returns
214  * right away. When all data is sent, the send callback function is called.
215  *
216  * param base UART peripheral base address.
217  * param handle UART handle pointer.
218  * param xfer UART DMA transfer structure. See #uart_transfer_t.
219  * retval kStatus_Success if succeeded; otherwise failed.
220  * retval kStatus_UART_TxBusy Previous transfer ongoing.
221  * retval kStatus_InvalidArgument Invalid argument.
222  */
UART_TransferSendDMA(UART_Type * base,uart_dma_handle_t * handle,uart_transfer_t * xfer)223 status_t UART_TransferSendDMA(UART_Type *base, uart_dma_handle_t *handle, uart_transfer_t *xfer)
224 {
225     assert(handle != NULL);
226     assert(handle->txDmaHandle != NULL);
227     assert(xfer != NULL);
228     assert(xfer->data != NULL);
229     assert(xfer->dataSize != 0U);
230 
231     dma_transfer_config_t xferConfig;
232     status_t status;
233 
234     /* If previous TX not finished. */
235     if ((uint8_t)kUART_TxBusy == handle->txState)
236     {
237         status = kStatus_UART_TxBusy;
238     }
239     else
240     {
241         handle->txState       = (uint8_t)kUART_TxBusy;
242         handle->txDataSizeAll = xfer->dataSize;
243 
244         /* Prepare transfer. */
245         DMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t), (uint32_t *)UART_GetDataRegisterAddress(base),
246                             sizeof(uint8_t), xfer->dataSize, kDMA_MemoryToPeripheral);
247 
248         /* Submit transfer. */
249         (void)DMA_SubmitTransfer(handle->txDmaHandle, &xferConfig, (uint32_t)kDMA_EnableInterrupt);
250         DMA_StartTransfer(handle->txDmaHandle);
251 
252         /* Enable UART TX DMA. */
253         UART_EnableTxDMA(base, true);
254 
255         status = kStatus_Success;
256     }
257 
258     return status;
259 }
260 
261 /*!
262  * brief Receives data using DMA.
263  *
264  * This function receives data using DMA. This is non-blocking function, which returns
265  * right away. When all data is received, the receive callback function is called.
266  *
267  * param base UART peripheral base address.
268  * param handle Pointer to the uart_dma_handle_t structure.
269  * param xfer UART DMA transfer structure. See #uart_transfer_t.
270  * retval kStatus_Success if succeeded; otherwise failed.
271  * retval kStatus_UART_RxBusy Previous transfer on going.
272  * retval kStatus_InvalidArgument Invalid argument.
273  */
UART_TransferReceiveDMA(UART_Type * base,uart_dma_handle_t * handle,uart_transfer_t * xfer)274 status_t UART_TransferReceiveDMA(UART_Type *base, uart_dma_handle_t *handle, uart_transfer_t *xfer)
275 {
276     assert(handle != NULL);
277     assert(handle->rxDmaHandle != NULL);
278     assert(xfer != NULL);
279     assert(xfer->data != NULL);
280     assert(xfer->dataSize != 0U);
281 
282     dma_transfer_config_t xferConfig;
283     status_t status;
284 
285     /* If previous RX not finished. */
286     if ((uint8_t)kUART_RxBusy == handle->rxState)
287     {
288         status = kStatus_UART_RxBusy;
289     }
290     else
291     {
292         handle->rxState       = (uint8_t)kUART_RxBusy;
293         handle->rxDataSizeAll = xfer->dataSize;
294 
295         /* Prepare transfer. */
296         DMA_PrepareTransfer(&xferConfig, (uint32_t *)UART_GetDataRegisterAddress(base), sizeof(uint8_t), xfer->data,
297                             sizeof(uint8_t), xfer->dataSize, kDMA_PeripheralToMemory);
298 
299         /* Submit transfer. */
300         (void)DMA_SubmitTransfer(handle->rxDmaHandle, &xferConfig, (uint32_t)kDMA_EnableInterrupt);
301         DMA_StartTransfer(handle->rxDmaHandle);
302 
303         /* Enable UART RX DMA. */
304         UART_EnableRxDMA(base, true);
305 
306         status = kStatus_Success;
307     }
308 
309     return status;
310 }
311 
312 /*!
313  * brief Aborts the send data using DMA.
314  *
315  * This function aborts the sent data using DMA.
316  *
317  * param base UART peripheral base address.
318  * param handle Pointer to uart_dma_handle_t structure.
319  */
UART_TransferAbortSendDMA(UART_Type * base,uart_dma_handle_t * handle)320 void UART_TransferAbortSendDMA(UART_Type *base, uart_dma_handle_t *handle)
321 {
322     assert(handle != NULL);
323     assert(handle->txDmaHandle != NULL);
324 
325     /* Disable UART TX DMA. */
326     UART_EnableTxDMA(base, false);
327 
328     /* Stop transfer. */
329     DMA_AbortTransfer(handle->txDmaHandle);
330 
331     /* Write DMA->DSR[DONE] to abort transfer and clear status. */
332     DMA_ClearChannelStatusFlags(handle->txDmaHandle->base, handle->txDmaHandle->channel,
333                                 (uint32_t)kDMA_TransactionsDoneFlag);
334 
335     handle->txState = (uint8_t)kUART_TxIdle;
336 }
337 
338 /*!
339  * brief Aborts the received data using DMA.
340  *
341  * This function abort receive data which using DMA.
342  *
343  * param base UART peripheral base address.
344  * param handle Pointer to uart_dma_handle_t structure.
345  */
UART_TransferAbortReceiveDMA(UART_Type * base,uart_dma_handle_t * handle)346 void UART_TransferAbortReceiveDMA(UART_Type *base, uart_dma_handle_t *handle)
347 {
348     assert(handle != NULL);
349     assert(handle->rxDmaHandle != NULL);
350 
351     /* Disable UART RX DMA. */
352     UART_EnableRxDMA(base, false);
353 
354     /* Stop transfer. */
355     DMA_AbortTransfer(handle->rxDmaHandle);
356 
357     /* Write DMA->DSR[DONE] to abort transfer and clear status. */
358     DMA_ClearChannelStatusFlags(handle->rxDmaHandle->base, handle->rxDmaHandle->channel,
359                                 (uint32_t)kDMA_TransactionsDoneFlag);
360 
361     handle->rxState = (uint8_t)kUART_RxIdle;
362 }
363 
364 /*!
365  * brief Gets the number of bytes written to UART TX register.
366  *
367  * This function gets the number of bytes written to UART TX
368  * register by DMA.
369  *
370  * param base UART peripheral base address.
371  * param handle UART handle pointer.
372  * param count Send bytes count.
373  * retval kStatus_NoTransferInProgress No send in progress.
374  * retval kStatus_InvalidArgument Parameter is invalid.
375  * retval kStatus_Success Get successfully through the parameter \p count;
376  */
UART_TransferGetSendCountDMA(UART_Type * base,uart_dma_handle_t * handle,uint32_t * count)377 status_t UART_TransferGetSendCountDMA(UART_Type *base, uart_dma_handle_t *handle, uint32_t *count)
378 {
379     assert(handle != NULL);
380     assert(handle->txDmaHandle != NULL);
381     assert(count != NULL);
382 
383     if ((uint8_t)kUART_TxIdle == handle->txState)
384     {
385         return kStatus_NoTransferInProgress;
386     }
387 
388     *count = handle->txDataSizeAll - DMA_GetRemainingBytes(handle->txDmaHandle->base, handle->txDmaHandle->channel);
389 
390     return kStatus_Success;
391 }
392 
393 /*!
394  * brief Gets the number of bytes that have been received.
395  *
396  * This function gets the number of bytes that have been received.
397  *
398  * param base UART peripheral base address.
399  * param handle UART handle pointer.
400  * param count Receive bytes count.
401  * retval kStatus_NoTransferInProgress No receive in progress.
402  * retval kStatus_InvalidArgument Parameter is invalid.
403  * retval kStatus_Success Get successfully through the parameter \p count;
404  */
UART_TransferGetReceiveCountDMA(UART_Type * base,uart_dma_handle_t * handle,uint32_t * count)405 status_t UART_TransferGetReceiveCountDMA(UART_Type *base, uart_dma_handle_t *handle, uint32_t *count)
406 {
407     assert(handle != NULL);
408     assert(handle->rxDmaHandle != NULL);
409     assert(count != NULL);
410 
411     if ((uint8_t)kUART_RxIdle == handle->rxState)
412     {
413         return kStatus_NoTransferInProgress;
414     }
415 
416     *count = handle->rxDataSizeAll - DMA_GetRemainingBytes(handle->rxDmaHandle->base, handle->rxDmaHandle->channel);
417 
418     return kStatus_Success;
419 }
420 
UART_TransferDMAHandleIRQ(UART_Type * base,void * uartDmaHandle)421 void UART_TransferDMAHandleIRQ(UART_Type *base, void *uartDmaHandle)
422 {
423     assert(uartDmaHandle != NULL);
424 
425     uart_dma_handle_t *handle = (uart_dma_handle_t *)uartDmaHandle;
426 
427     handle->txState = (uint8_t)kUART_TxIdle;
428 
429     /* Disable tx complete interrupt */
430     UART_DisableInterrupts(base, (uint32_t)kUART_TransmissionCompleteInterruptEnable);
431 
432     if (handle->callback != NULL)
433     {
434         handle->callback(base, handle, kStatus_UART_TxIdle, handle->userData);
435     }
436 }
437