1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2021 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_uart_edma.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_edma"
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_edma_private_handle_t. The structure is private. */
48 typedef struct _uart_edma_private_handle
49 {
50 UART_Type *base;
51 uart_edma_handle_t *handle;
52 } uart_edma_private_handle_t;
53
54 /* UART EDMA 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_edma_private_handle_t s_edmaPrivateHandle[UART_HANDLE_ARRAY_SIZE];
69
70 /*******************************************************************************
71 * Prototypes
72 ******************************************************************************/
73
74 /*!
75 * @brief UART EDMA send finished callback function.
76 *
77 * This function is called when UART EDMA send finished. It disables the UART
78 * TX EDMA request and sends @ref kStatus_UART_TxIdle to UART callback.
79 *
80 * @param handle The EDMA handle.
81 * @param param Callback function parameter.
82 */
83 static void UART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
84
85 /*!
86 * @brief UART EDMA receive finished callback function.
87 *
88 * This function is called when UART EDMA receive finished. It disables the UART
89 * RX EDMA request and sends @ref kStatus_UART_RxIdle to UART callback.
90 *
91 * @param handle The EDMA handle.
92 * @param param Callback function parameter.
93 */
94 static void UART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
95
96 /*******************************************************************************
97 * Code
98 ******************************************************************************/
99
UART_SendEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)100 static void UART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
101 {
102 assert(param != NULL);
103
104 uart_edma_private_handle_t *uartPrivateHandle = (uart_edma_private_handle_t *)param;
105
106 /* Avoid the warning for unused variables. */
107 handle = handle;
108 tcds = tcds;
109
110 if (transferDone)
111 {
112 /* Disable UART TX EDMA. */
113 UART_EnableTxDMA(uartPrivateHandle->base, false);
114 /* Stop transfer. */
115 EDMA_AbortTransfer(handle);
116
117 /* Enable tx complete interrupt */
118 UART_EnableInterrupts(uartPrivateHandle->base, (uint32_t)kUART_TransmissionCompleteInterruptEnable);
119 }
120 }
121
UART_ReceiveEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)122 static void UART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
123 {
124 assert(param != NULL);
125
126 uart_edma_private_handle_t *uartPrivateHandle = (uart_edma_private_handle_t *)param;
127
128 /* Avoid warning for unused parameters. */
129 handle = handle;
130 tcds = tcds;
131
132 if (transferDone)
133 {
134 /* Disable transfer. */
135 UART_TransferAbortReceiveEDMA(uartPrivateHandle->base, uartPrivateHandle->handle);
136
137 if (uartPrivateHandle->handle->callback != NULL)
138 {
139 uartPrivateHandle->handle->callback(uartPrivateHandle->base, uartPrivateHandle->handle, kStatus_UART_RxIdle,
140 uartPrivateHandle->handle->userData);
141 }
142 }
143 }
144
145 /*!
146 * brief Initializes the UART handle which is used in transactional functions.
147 * param base UART peripheral base address.
148 * param handle Pointer to the uart_edma_handle_t structure.
149 * param callback UART callback, NULL means no callback.
150 * param userData User callback function data.
151 * param rxEdmaHandle User-requested DMA handle for RX DMA transfer.
152 * param txEdmaHandle User-requested DMA handle for TX DMA transfer.
153 */
UART_TransferCreateHandleEDMA(UART_Type * base,uart_edma_handle_t * handle,uart_edma_transfer_callback_t callback,void * userData,edma_handle_t * txEdmaHandle,edma_handle_t * rxEdmaHandle)154 void UART_TransferCreateHandleEDMA(UART_Type *base,
155 uart_edma_handle_t *handle,
156 uart_edma_transfer_callback_t callback,
157 void *userData,
158 edma_handle_t *txEdmaHandle,
159 edma_handle_t *rxEdmaHandle)
160 {
161 assert(handle != NULL);
162
163 uint32_t instance = UART_GetInstance(base);
164
165 s_edmaPrivateHandle[instance].base = base;
166 s_edmaPrivateHandle[instance].handle = handle;
167
168 (void)memset(handle, 0, sizeof(*handle));
169
170 handle->rxState = (uint8_t)kUART_RxIdle;
171 handle->txState = (uint8_t)kUART_TxIdle;
172
173 handle->rxEdmaHandle = rxEdmaHandle;
174 handle->txEdmaHandle = txEdmaHandle;
175
176 handle->callback = callback;
177 handle->userData = userData;
178
179 #if defined(FSL_FEATURE_UART_HAS_FIFO) && FSL_FEATURE_UART_HAS_FIFO
180 /* Note:
181 Take care of the RX FIFO, EDMA request only assert when received bytes
182 equal or more than RX water mark, there is potential issue if RX water
183 mark larger than 1.
184 For example, if RX FIFO water mark is 2, upper layer needs 5 bytes and
185 5 bytes are received. the last byte will be saved in FIFO but not trigger
186 EDMA transfer because the water mark is 2.
187 */
188 if (rxEdmaHandle != NULL)
189 {
190 base->RWFIFO = 1U;
191 }
192 #endif
193
194 /* Save the handle in global variables to support the double weak mechanism. */
195 s_uartHandle[instance] = handle;
196 /* Save IRQ handler into static ISR function pointer. */
197 s_uartIsr = UART_TransferEdmaHandleIRQ;
198 /* Disable internal IRQs and enable global IRQ. */
199 UART_DisableInterrupts(base, (uint32_t)kUART_AllInterruptsEnable);
200 (void)EnableIRQ(s_uartIRQ[instance]);
201
202 /* Configure TX. */
203 if (txEdmaHandle != NULL)
204 {
205 EDMA_SetCallback(handle->txEdmaHandle, UART_SendEDMACallback, &s_edmaPrivateHandle[instance]);
206 }
207
208 /* Configure RX. */
209 if (rxEdmaHandle != NULL)
210 {
211 EDMA_SetCallback(handle->rxEdmaHandle, UART_ReceiveEDMACallback, &s_edmaPrivateHandle[instance]);
212 }
213 }
214
215 /*!
216 * brief Sends data using eDMA.
217 *
218 * This function sends data using eDMA. This is a non-blocking function, which returns
219 * right away. When all data is sent, the send callback function is called.
220 *
221 * param base UART peripheral base address.
222 * param handle UART handle pointer.
223 * param xfer UART eDMA transfer structure. See #uart_transfer_t.
224 * retval kStatus_Success if succeeded; otherwise failed.
225 * retval kStatus_UART_TxBusy Previous transfer ongoing.
226 * retval kStatus_InvalidArgument Invalid argument.
227 */
UART_SendEDMA(UART_Type * base,uart_edma_handle_t * handle,uart_transfer_t * xfer)228 status_t UART_SendEDMA(UART_Type *base, uart_edma_handle_t *handle, uart_transfer_t *xfer)
229 {
230 assert(handle != NULL);
231 assert(handle->txEdmaHandle != NULL);
232 assert(xfer != NULL);
233 assert(xfer->data != NULL);
234 assert(xfer->dataSize != 0U);
235
236 edma_transfer_config_t xferConfig;
237 status_t status;
238
239 /* If previous TX not finished. */
240 if ((uint8_t)kUART_TxBusy == handle->txState)
241 {
242 status = kStatus_UART_TxBusy;
243 }
244 else
245 {
246 handle->txState = (uint8_t)kUART_TxBusy;
247 handle->txDataSizeAll = xfer->dataSize;
248
249 /* Prepare transfer. */
250 EDMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t), (uint32_t *)UART_GetDataRegisterAddress(base),
251 sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_MemoryToPeripheral);
252
253 /* Store the initially configured eDMA minor byte transfer count into the UART handle */
254 handle->nbytes = 1U;
255
256 /* Submit transfer. */
257 (void)EDMA_SubmitTransfer(handle->txEdmaHandle, &xferConfig);
258 EDMA_StartTransfer(handle->txEdmaHandle);
259
260 /* Enable UART TX EDMA. */
261 UART_EnableTxDMA(base, true);
262
263 status = kStatus_Success;
264 }
265
266 return status;
267 }
268
269 /*!
270 * brief Receives data using eDMA.
271 *
272 * This function receives data using eDMA. This is a non-blocking function, which returns
273 * right away. When all data is received, the receive callback function is called.
274 *
275 * param base UART peripheral base address.
276 * param handle Pointer to the uart_edma_handle_t structure.
277 * param xfer UART eDMA transfer structure. See #uart_transfer_t.
278 * retval kStatus_Success if succeeded; otherwise failed.
279 * retval kStatus_UART_RxBusy Previous transfer ongoing.
280 * retval kStatus_InvalidArgument Invalid argument.
281 */
UART_ReceiveEDMA(UART_Type * base,uart_edma_handle_t * handle,uart_transfer_t * xfer)282 status_t UART_ReceiveEDMA(UART_Type *base, uart_edma_handle_t *handle, uart_transfer_t *xfer)
283 {
284 assert(handle != NULL);
285 assert(handle->rxEdmaHandle != NULL);
286 assert(xfer != NULL);
287 assert(xfer->data != NULL);
288 assert(xfer->dataSize != 0U);
289
290 edma_transfer_config_t xferConfig;
291 status_t status;
292
293 /* If previous RX not finished. */
294 if ((uint8_t)kUART_RxBusy == handle->rxState)
295 {
296 status = kStatus_UART_RxBusy;
297 }
298 else
299 {
300 handle->rxState = (uint8_t)kUART_RxBusy;
301 handle->rxDataSizeAll = xfer->dataSize;
302
303 /* Prepare transfer. */
304 EDMA_PrepareTransfer(&xferConfig, (uint32_t *)UART_GetDataRegisterAddress(base), sizeof(uint8_t), xfer->data,
305 sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_PeripheralToMemory);
306
307 /* Store the initially configured eDMA minor byte transfer count into the UART handle */
308 handle->nbytes = 1U;
309
310 /* Submit transfer. */
311 (void)EDMA_SubmitTransfer(handle->rxEdmaHandle, &xferConfig);
312 EDMA_StartTransfer(handle->rxEdmaHandle);
313
314 /* Enable UART RX EDMA. */
315 UART_EnableRxDMA(base, true);
316
317 status = kStatus_Success;
318 }
319
320 return status;
321 }
322
323 /*!
324 * brief Aborts the sent data using eDMA.
325 *
326 * This function aborts sent data using eDMA.
327 *
328 * param base UART peripheral base address.
329 * param handle Pointer to the uart_edma_handle_t structure.
330 */
UART_TransferAbortSendEDMA(UART_Type * base,uart_edma_handle_t * handle)331 void UART_TransferAbortSendEDMA(UART_Type *base, uart_edma_handle_t *handle)
332 {
333 assert(handle != NULL);
334 assert(handle->txEdmaHandle != NULL);
335
336 /* Disable UART TX EDMA. */
337 UART_EnableTxDMA(base, false);
338
339 /* Stop transfer. */
340 EDMA_AbortTransfer(handle->txEdmaHandle);
341
342 handle->txState = (uint8_t)kUART_TxIdle;
343 }
344
345 /*!
346 * brief Aborts the receive data using eDMA.
347 *
348 * This function aborts receive data using eDMA.
349 *
350 * param base UART peripheral base address.
351 * param handle Pointer to the uart_edma_handle_t structure.
352 */
UART_TransferAbortReceiveEDMA(UART_Type * base,uart_edma_handle_t * handle)353 void UART_TransferAbortReceiveEDMA(UART_Type *base, uart_edma_handle_t *handle)
354 {
355 assert(handle != NULL);
356 assert(handle->rxEdmaHandle != NULL);
357
358 /* Disable UART RX EDMA. */
359 UART_EnableRxDMA(base, false);
360
361 /* Stop transfer. */
362 EDMA_AbortTransfer(handle->rxEdmaHandle);
363
364 handle->rxState = (uint8_t)kUART_RxIdle;
365 }
366
367 /*!
368 * brief Gets the number of received bytes.
369 *
370 * This function gets the number of received bytes.
371 *
372 * param base UART peripheral base address.
373 * param handle UART handle pointer.
374 * param count Receive bytes count.
375 * retval kStatus_NoTransferInProgress No receive in progress.
376 * retval kStatus_InvalidArgument Parameter is invalid.
377 * retval kStatus_Success Get successfully through the parameter \p count;
378 */
UART_TransferGetReceiveCountEDMA(UART_Type * base,uart_edma_handle_t * handle,uint32_t * count)379 status_t UART_TransferGetReceiveCountEDMA(UART_Type *base, uart_edma_handle_t *handle, uint32_t *count)
380 {
381 assert(handle != NULL);
382 assert(handle->rxEdmaHandle != NULL);
383 assert(count != NULL);
384
385 if ((uint8_t)kUART_RxIdle == handle->rxState)
386 {
387 return kStatus_NoTransferInProgress;
388 }
389
390 *count = handle->rxDataSizeAll -
391 (uint32_t)handle->nbytes *
392 EDMA_GetRemainingMajorLoopCount(handle->rxEdmaHandle->base, handle->rxEdmaHandle->channel);
393
394 return kStatus_Success;
395 }
396
397 /*!
398 * brief Gets the number of bytes that have been written to UART TX register.
399 *
400 * This function gets the number of bytes that have been written to UART TX
401 * register by DMA.
402 *
403 * param base UART peripheral base address.
404 * param handle UART handle pointer.
405 * param count Send bytes count.
406 * retval kStatus_NoTransferInProgress No send in progress.
407 * retval kStatus_InvalidArgument Parameter is invalid.
408 * retval kStatus_Success Get successfully through the parameter \p count;
409 */
UART_TransferGetSendCountEDMA(UART_Type * base,uart_edma_handle_t * handle,uint32_t * count)410 status_t UART_TransferGetSendCountEDMA(UART_Type *base, uart_edma_handle_t *handle, uint32_t *count)
411 {
412 assert(handle != NULL);
413 assert(handle->txEdmaHandle != NULL);
414 assert(count != NULL);
415
416 if ((uint8_t)kUART_TxIdle == handle->txState)
417 {
418 return kStatus_NoTransferInProgress;
419 }
420
421 *count = handle->txDataSizeAll -
422 (uint32_t)handle->nbytes *
423 EDMA_GetRemainingMajorLoopCount(handle->txEdmaHandle->base, handle->txEdmaHandle->channel);
424
425 return kStatus_Success;
426 }
427
428 /*!
429 * brief UART eDMA IRQ handle function.
430 *
431 * This function handles the UART transmit complete IRQ request and invoke user callback.
432 *
433 * param base UART peripheral base address.
434 * param uartEdmaHandle UART handle pointer.
435 */
UART_TransferEdmaHandleIRQ(UART_Type * base,void * uartEdmaHandle)436 void UART_TransferEdmaHandleIRQ(UART_Type *base, void *uartEdmaHandle)
437 {
438 assert(uartEdmaHandle != NULL);
439
440 uart_edma_handle_t *handle = (uart_edma_handle_t *)uartEdmaHandle;
441
442 handle->txState = (uint8_t)kUART_TxIdle;
443
444 /* Disable tx complete interrupt */
445 UART_DisableInterrupts(base, (uint32_t)kUART_TransmissionCompleteInterruptEnable);
446
447 if (handle->callback != NULL)
448 {
449 handle->callback(base, handle, kStatus_UART_TxIdle, handle->userData);
450 }
451 }
452