1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2022 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_lpuart_edma.h"
10 /*
11  * $Coverage Justification Reference$
12  *
13  * $Justification fsl_lpuart_edma_c_ref_1$
14  * The EDMA handle is only used by the LPUART EDMA driver, with the LPUART EDMA driver workflow,
15  * the callback is only called when EDMA transfer done.
16  *
17  * $Justification fsl_lpuart_edma_c_ref_2$
18  * This function only handles the kLPUART_TransmissionCompleteFlag event.
19  *
20  */
21 /*******************************************************************************
22  * Definitions
23  ******************************************************************************/
24 
25 /* Component ID definition, used by tools. */
26 #ifndef FSL_COMPONENT_ID
27 #define FSL_COMPONENT_ID "platform.drivers.lpuart_edma"
28 #endif
29 
30 /*<! Structure definition for lpuart_edma_private_handle_t. The structure is private. */
31 typedef struct _lpuart_edma_private_handle
32 {
33     LPUART_Type *base;
34     lpuart_edma_handle_t *handle;
35 } lpuart_edma_private_handle_t;
36 
37 /* LPUART EDMA transfer handle. */
38 enum
39 {
40     kLPUART_TxIdle, /* TX idle. */
41     kLPUART_TxBusy, /* TX busy. */
42     kLPUART_RxIdle, /* RX idle. */
43     kLPUART_RxBusy  /* RX busy. */
44 };
45 
46 /*******************************************************************************
47  * Variables
48  ******************************************************************************/
49 
50 /* Array of LPUART handle. */
51 #if (defined(LPUART12))
52 #define LPUART_HANDLE_ARRAY_SIZE 13
53 #else /* LPUART12 */
54 #if (defined(LPUART11))
55 #define LPUART_HANDLE_ARRAY_SIZE 12
56 #else /* LPUART11 */
57 #if (defined(LPUART10))
58 #define LPUART_HANDLE_ARRAY_SIZE 11
59 #else /* LPUART10 */
60 #if (defined(LPUART9))
61 #define LPUART_HANDLE_ARRAY_SIZE 10
62 #else /* LPUART9 */
63 #if (defined(LPUART8))
64 #define LPUART_HANDLE_ARRAY_SIZE 9
65 #else /* LPUART8 */
66 #if (defined(LPUART7))
67 #define LPUART_HANDLE_ARRAY_SIZE 8
68 #else /* LPUART7 */
69 #if (defined(LPUART6))
70 #define LPUART_HANDLE_ARRAY_SIZE 7
71 #else /* LPUART6 */
72 #if (defined(LPUART5))
73 #define LPUART_HANDLE_ARRAY_SIZE 6
74 #else /* LPUART5 */
75 #if (defined(LPUART4))
76 #define LPUART_HANDLE_ARRAY_SIZE 5
77 #else /* LPUART4 */
78 #if (defined(LPUART3))
79 #define LPUART_HANDLE_ARRAY_SIZE 4
80 #else /* LPUART3 */
81 #if (defined(LPUART2))
82 #define LPUART_HANDLE_ARRAY_SIZE 3
83 #else /* LPUART2 */
84 #if (defined(LPUART1))
85 #define LPUART_HANDLE_ARRAY_SIZE 2
86 #else /* LPUART1 */
87 #if (defined(LPUART0))
88 #define LPUART_HANDLE_ARRAY_SIZE 1
89 #else /* LPUART0 */
90 #define LPUART_HANDLE_ARRAY_SIZE FSL_FEATURE_SOC_LPUART_COUNT
91 #endif /* LPUART 0 */
92 #endif /* LPUART 1 */
93 #endif /* LPUART 2 */
94 #endif /* LPUART 3 */
95 #endif /* LPUART 4 */
96 #endif /* LPUART 5 */
97 #endif /* LPUART 6 */
98 #endif /* LPUART 7 */
99 #endif /* LPUART 8 */
100 #endif /* LPUART 9 */
101 #endif /* LPUART 10 */
102 #endif /* LPUART 11 */
103 #endif /* LPUART 12 */
104 
105 /*<! Private handle only used for internally. */
106 static lpuart_edma_private_handle_t s_lpuartEdmaPrivateHandle[LPUART_HANDLE_ARRAY_SIZE];
107 
108 /*******************************************************************************
109  * Prototypes
110  ******************************************************************************/
111 
112 /*!
113  * @brief LPUART EDMA send finished callback function.
114  *
115  * This function is called when LPUART EDMA send finished. It disables the LPUART
116  * TX EDMA request and sends @ref kStatus_LPUART_TxIdle to LPUART callback.
117  *
118  * @param handle The EDMA handle.
119  * @param param Callback function parameter.
120  */
121 static void LPUART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
122 
123 /*!
124  * @brief LPUART EDMA receive finished callback function.
125  *
126  * This function is called when LPUART EDMA receive finished. It disables the LPUART
127  * RX EDMA request and sends @ref kStatus_LPUART_RxIdle to LPUART callback.
128  *
129  * @param handle The EDMA handle.
130  * @param param Callback function parameter.
131  */
132 static void LPUART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
133 
134 /*******************************************************************************
135  * Code
136  ******************************************************************************/
137 
LPUART_SendEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)138 static void LPUART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
139 {
140     assert(NULL != param);
141 
142     lpuart_edma_private_handle_t *lpuartPrivateHandle = (lpuart_edma_private_handle_t *)param;
143 
144     /* Avoid the warning for unused variables. */
145     handle = handle;
146     tcds   = tcds;
147     /*
148      * $Branch Coverage Justification$
149      * $ref fsl_lpuart_edma_c_ref_1$
150      */
151     if (transferDone)
152     {
153         /* Disable LPUART TX EDMA. */
154         LPUART_EnableTxDMA(lpuartPrivateHandle->base, false);
155 
156         /* Stop transfer. */
157         EDMA_AbortTransfer(handle);
158 
159         /* Enable tx complete interrupt */
160         LPUART_EnableInterrupts(lpuartPrivateHandle->base, (uint32_t)kLPUART_TransmissionCompleteInterruptEnable);
161     }
162 }
163 
LPUART_ReceiveEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)164 static void LPUART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
165 {
166     assert(NULL != param);
167 
168     lpuart_edma_private_handle_t *lpuartPrivateHandle = (lpuart_edma_private_handle_t *)param;
169 
170     /* Avoid warning for unused parameters. */
171     handle = handle;
172     tcds   = tcds;
173     /*
174      * $Branch Coverage Justification$
175      * $ref fsl_lpuart_edma_c_ref_1$
176      */
177     if (transferDone)
178     {
179         /* Disable transfer. */
180         LPUART_TransferAbortReceiveEDMA(lpuartPrivateHandle->base, lpuartPrivateHandle->handle);
181 
182         if (NULL != lpuartPrivateHandle->handle->callback)
183         {
184             lpuartPrivateHandle->handle->callback(lpuartPrivateHandle->base, lpuartPrivateHandle->handle,
185                                                   kStatus_LPUART_RxIdle, lpuartPrivateHandle->handle->userData);
186         }
187     }
188 }
189 
190 /*!
191  * brief Initializes the LPUART handle which is used in transactional functions.
192  *
193  * note This function disables all LPUART interrupts.
194  *
195  * param base LPUART peripheral base address.
196  * param handle Pointer to lpuart_edma_handle_t structure.
197  * param callback Callback function.
198  * param userData User data.
199  * param txEdmaHandle User requested DMA handle for TX DMA transfer.
200  * param rxEdmaHandle User requested DMA handle for RX DMA transfer.
201  */
LPUART_TransferCreateHandleEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,lpuart_edma_transfer_callback_t callback,void * userData,edma_handle_t * txEdmaHandle,edma_handle_t * rxEdmaHandle)202 void LPUART_TransferCreateHandleEDMA(LPUART_Type *base,
203                                      lpuart_edma_handle_t *handle,
204                                      lpuart_edma_transfer_callback_t callback,
205                                      void *userData,
206                                      edma_handle_t *txEdmaHandle,
207                                      edma_handle_t *rxEdmaHandle)
208 {
209     assert(NULL != handle);
210 
211     uint32_t instance = LPUART_GetInstance(base);
212 
213     s_lpuartEdmaPrivateHandle[instance].base   = base;
214     s_lpuartEdmaPrivateHandle[instance].handle = handle;
215 
216     (void)memset(handle, 0, sizeof(*handle));
217 
218     handle->rxState = (uint8_t)kLPUART_RxIdle;
219     handle->txState = (uint8_t)kLPUART_TxIdle;
220 
221     handle->rxEdmaHandle = rxEdmaHandle;
222     handle->txEdmaHandle = txEdmaHandle;
223 
224     handle->callback = callback;
225     handle->userData = userData;
226 
227 #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO
228     /* Note:
229        Take care of the RX FIFO, EDMA request only assert when received bytes
230        equal or more than RX water mark, there is potential issue if RX water
231        mark larger than 1.
232        For example, if RX FIFO water mark is 2, upper layer needs 5 bytes and
233        5 bytes are received. the last byte will be saved in FIFO but not trigger
234        EDMA transfer because the water mark is 2.
235      */
236     if (NULL != rxEdmaHandle)
237     {
238         base->WATER &= (~LPUART_WATER_RXWATER_MASK);
239     }
240 #endif
241 
242     /* Save the handle in global variables to support the double weak mechanism. */
243     s_lpuartHandle[instance] = handle;
244     /* Set LPUART_TransferEdmaHandleIRQ as DMA IRQ handler */
245     s_lpuartIsr[instance] = LPUART_TransferEdmaHandleIRQ;
246     /* Disable all LPUART internal interrupts */
247     LPUART_DisableInterrupts(base, (uint32_t)kLPUART_AllInterruptEnable);
248     /* Enable interrupt in NVIC. */
249 #if defined(FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ) && FSL_FEATURE_LPUART_HAS_SEPARATE_RX_TX_IRQ
250     (void)EnableIRQ(s_lpuartTxIRQ[instance]);
251 #else
252     (void)EnableIRQ(s_lpuartIRQ[instance]);
253 #endif
254 
255     /* Configure TX. */
256     if (NULL != txEdmaHandle)
257     {
258         EDMA_SetCallback(handle->txEdmaHandle, LPUART_SendEDMACallback, &s_lpuartEdmaPrivateHandle[instance]);
259     }
260 
261     /* Configure RX. */
262     if (NULL != rxEdmaHandle)
263     {
264         EDMA_SetCallback(handle->rxEdmaHandle, LPUART_ReceiveEDMACallback, &s_lpuartEdmaPrivateHandle[instance]);
265     }
266 }
267 
268 /*!
269  * brief Sends data using eDMA.
270  *
271  * This function sends data using eDMA. This is a non-blocking function, which returns
272  * right away. When all data is sent, the send callback function is called.
273  *
274  * param base LPUART peripheral base address.
275  * param handle LPUART handle pointer.
276  * param xfer LPUART eDMA transfer structure. See #lpuart_transfer_t.
277  * retval kStatus_Success if succeed, others failed.
278  * retval kStatus_LPUART_TxBusy Previous transfer on going.
279  * retval kStatus_InvalidArgument Invalid argument.
280  */
LPUART_SendEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,lpuart_transfer_t * xfer)281 status_t LPUART_SendEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, lpuart_transfer_t *xfer)
282 {
283     assert(NULL != handle);
284     assert(NULL != handle->txEdmaHandle);
285     assert(NULL != xfer);
286     assert(NULL != xfer->data);
287     assert(0U != xfer->dataSize);
288 
289     edma_transfer_config_t xferConfig;
290     status_t status;
291 
292     /* If previous TX not finished. */
293     if ((uint8_t)kLPUART_TxBusy == handle->txState)
294     {
295         status = kStatus_LPUART_TxBusy;
296     }
297     else
298     {
299         handle->txState       = (uint8_t)kLPUART_TxBusy;
300         handle->txDataSizeAll = xfer->dataSize;
301 
302         /* Prepare transfer. */
303         EDMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t),
304                              (void *)(uint32_t *)LPUART_GetDataRegisterAddress(base), sizeof(uint8_t), sizeof(uint8_t),
305                              xfer->dataSize, kEDMA_MemoryToPeripheral);
306 
307         /* Store the initially configured eDMA minor byte transfer count into the LPUART handle */
308         handle->nbytes = (uint8_t)sizeof(uint8_t);
309 
310         /* Submit transfer. */
311         if (kStatus_Success !=
312             EDMA_SubmitTransfer(handle->txEdmaHandle, (const edma_transfer_config_t *)(uint32_t)&xferConfig))
313         {
314             return kStatus_Fail;
315         }
316         EDMA_StartTransfer(handle->txEdmaHandle);
317 
318         /* Enable LPUART TX EDMA. */
319         LPUART_EnableTxDMA(base, true);
320 
321         status = kStatus_Success;
322     }
323 
324     return status;
325 }
326 
327 /*!
328  * brief Receives data using eDMA.
329  *
330  * This function receives data using eDMA. This is non-blocking function, which returns
331  * right away. When all data is received, the receive callback function is called.
332  *
333  * param base LPUART peripheral base address.
334  * param handle Pointer to lpuart_edma_handle_t structure.
335  * param xfer LPUART eDMA transfer structure, see #lpuart_transfer_t.
336  * retval kStatus_Success if succeed, others fail.
337  * retval kStatus_LPUART_RxBusy Previous transfer ongoing.
338  * retval kStatus_InvalidArgument Invalid argument.
339  */
LPUART_ReceiveEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,lpuart_transfer_t * xfer)340 status_t LPUART_ReceiveEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, lpuart_transfer_t *xfer)
341 {
342     assert(NULL != handle);
343     assert(NULL != handle->rxEdmaHandle);
344     assert(NULL != xfer);
345     assert(NULL != xfer->data);
346     assert(0U != xfer->dataSize);
347 
348     edma_transfer_config_t xferConfig;
349     status_t status;
350 
351     /* If previous RX not finished. */
352     if ((uint8_t)kLPUART_RxBusy == handle->rxState)
353     {
354         status = kStatus_LPUART_RxBusy;
355     }
356     else
357     {
358         handle->rxState       = (uint8_t)kLPUART_RxBusy;
359         handle->rxDataSizeAll = xfer->dataSize;
360 
361         /* Prepare transfer. */
362         EDMA_PrepareTransfer(&xferConfig, (void *)(uint32_t *)LPUART_GetDataRegisterAddress(base), sizeof(uint8_t),
363                              xfer->data, sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_PeripheralToMemory);
364 
365         /* Store the initially configured eDMA minor byte transfer count into the LPUART handle */
366         handle->nbytes = (uint8_t)sizeof(uint8_t);
367 
368         /* Submit transfer. */
369         if (kStatus_Success !=
370             EDMA_SubmitTransfer(handle->rxEdmaHandle, (const edma_transfer_config_t *)(uint32_t)&xferConfig))
371         {
372             return kStatus_Fail;
373         }
374         EDMA_StartTransfer(handle->rxEdmaHandle);
375 
376         /* Enable LPUART RX EDMA. */
377         LPUART_EnableRxDMA(base, true);
378 
379         status = kStatus_Success;
380     }
381 
382     return status;
383 }
384 
385 /*!
386  * brief Aborts the sent data using eDMA.
387  *
388  * This function aborts the sent data using eDMA.
389  *
390  * param base LPUART peripheral base address.
391  * param handle Pointer to lpuart_edma_handle_t structure.
392  */
LPUART_TransferAbortSendEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle)393 void LPUART_TransferAbortSendEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle)
394 {
395     assert(NULL != handle);
396     assert(NULL != handle->txEdmaHandle);
397 
398     /* Disable LPUART TX EDMA. */
399     LPUART_EnableTxDMA(base, false);
400 
401     /* Stop transfer. */
402     EDMA_AbortTransfer(handle->txEdmaHandle);
403 
404     handle->txState = (uint8_t)kLPUART_TxIdle;
405 }
406 
407 /*!
408  * brief Aborts the received data using eDMA.
409  *
410  * This function aborts the received data using eDMA.
411  *
412  * param base LPUART peripheral base address.
413  * param handle Pointer to lpuart_edma_handle_t structure.
414  */
LPUART_TransferAbortReceiveEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle)415 void LPUART_TransferAbortReceiveEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle)
416 {
417     assert(NULL != handle);
418     assert(NULL != handle->rxEdmaHandle);
419 
420     /* Disable LPUART RX EDMA. */
421     LPUART_EnableRxDMA(base, false);
422 
423     /* Stop transfer. */
424     EDMA_AbortTransfer(handle->rxEdmaHandle);
425 
426     handle->rxState = (uint8_t)kLPUART_RxIdle;
427 }
428 
429 /*!
430  * brief Gets the number of received bytes.
431  *
432  * This function gets the number of received bytes.
433  *
434  * param base LPUART peripheral base address.
435  * param handle LPUART handle pointer.
436  * param count Receive bytes count.
437  * retval kStatus_NoTransferInProgress No receive in progress.
438  * retval kStatus_InvalidArgument Parameter is invalid.
439  * retval kStatus_Success Get successfully through the parameter \p count;
440  */
LPUART_TransferGetReceiveCountEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,uint32_t * count)441 status_t LPUART_TransferGetReceiveCountEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, uint32_t *count)
442 {
443     assert(NULL != handle);
444     assert(NULL != handle->rxEdmaHandle);
445     assert(NULL != count);
446 
447     if ((uint8_t)kLPUART_RxIdle == handle->rxState)
448     {
449         return kStatus_NoTransferInProgress;
450     }
451 
452     *count = handle->rxDataSizeAll -
453              ((uint32_t)handle->nbytes *
454               EDMA_GetRemainingMajorLoopCount(handle->rxEdmaHandle->base, handle->rxEdmaHandle->channel));
455 
456     return kStatus_Success;
457 }
458 
459 /*!
460  * brief Gets the number of bytes written to the LPUART TX register.
461  *
462  * This function gets the number of bytes written to the LPUART TX
463  * register by DMA.
464  *
465  * param base LPUART peripheral base address.
466  * param handle LPUART handle pointer.
467  * param count Send bytes count.
468  * retval kStatus_NoTransferInProgress No send in progress.
469  * retval kStatus_InvalidArgument Parameter is invalid.
470  * retval kStatus_Success Get successfully through the parameter \p count;
471  */
LPUART_TransferGetSendCountEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,uint32_t * count)472 status_t LPUART_TransferGetSendCountEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, uint32_t *count)
473 {
474     assert(NULL != handle);
475     assert(NULL != handle->txEdmaHandle);
476     assert(NULL != count);
477 
478     if ((uint8_t)kLPUART_TxIdle == handle->txState)
479     {
480         return kStatus_NoTransferInProgress;
481     }
482 
483     *count = handle->txDataSizeAll -
484              ((uint32_t)handle->nbytes *
485               EDMA_GetRemainingMajorLoopCount(handle->txEdmaHandle->base, handle->txEdmaHandle->channel));
486 
487     return kStatus_Success;
488 }
489 
490 /*!
491  * brief LPUART eDMA IRQ handle function.
492  *
493  * This function handles the LPUART tx complete IRQ request and invoke user callback.
494  * It is not set to static so that it can be used in user application.
495  * note This function is used as default IRQ handler by double weak mechanism.
496  * If user's specific IRQ handler is implemented, make sure this function is invoked in the handler.
497  *
498  * param base LPUART peripheral base address.
499  * param lpuartEdmaHandle LPUART handle pointer.
500  */
LPUART_TransferEdmaHandleIRQ(LPUART_Type * base,void * lpuartEdmaHandle)501 void LPUART_TransferEdmaHandleIRQ(LPUART_Type *base, void *lpuartEdmaHandle)
502 {
503     assert(lpuartEdmaHandle != NULL);
504     /*
505      * $Branch Coverage Justification$
506      * $ref fsl_lpuart_edma_c_ref_2$
507      */
508     if (((uint32_t)kLPUART_TransmissionCompleteFlag & LPUART_GetStatusFlags(base)) != 0U)
509     {
510         lpuart_edma_handle_t *handle = (lpuart_edma_handle_t *)lpuartEdmaHandle;
511 
512         /* Disable tx complete interrupt */
513         LPUART_DisableInterrupts(base, (uint32_t)kLPUART_TransmissionCompleteInterruptEnable);
514 
515         handle->txState = (uint8_t)kLPUART_TxIdle;
516 
517         if (handle->callback != NULL)
518         {
519             handle->callback(base, handle, kStatus_LPUART_TxIdle, handle->userData);
520         }
521     }
522 }
523