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