1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_lpuart_edma.h"
10 
11 /*******************************************************************************
12  * Definitions
13  ******************************************************************************/
14 
15 /*<! Structure definition for lpuart_edma_private_handle_t. The structure is private. */
16 typedef struct _lpuart_edma_private_handle
17 {
18     LPUART_Type *base;
19     lpuart_edma_handle_t *handle;
20 } lpuart_edma_private_handle_t;
21 
22 /* LPUART EDMA transfer handle. */
23 enum _lpuart_edma_tansfer_states
24 {
25     kLPUART_TxIdle, /* TX idle. */
26     kLPUART_TxBusy, /* TX busy. */
27     kLPUART_RxIdle, /* RX idle. */
28     kLPUART_RxBusy  /* RX busy. */
29 };
30 
31 /*******************************************************************************
32  * Definitions
33  ******************************************************************************/
34 
35 /*<! Private handle only used for internally. */
36 static lpuart_edma_private_handle_t s_edmaPrivateHandle[FSL_FEATURE_SOC_LPUART_COUNT];
37 
38 /*******************************************************************************
39  * Prototypes
40  ******************************************************************************/
41 
42 /*!
43  * @brief LPUART EDMA send finished callback function.
44  *
45  * This function is called when LPUART EDMA send finished. It disables the LPUART
46  * TX EDMA request and sends @ref kStatus_LPUART_TxIdle to LPUART callback.
47  *
48  * @param handle The EDMA handle.
49  * @param param Callback function parameter.
50  */
51 static void LPUART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
52 
53 /*!
54  * @brief LPUART EDMA receive finished callback function.
55  *
56  * This function is called when LPUART EDMA receive finished. It disables the LPUART
57  * RX EDMA request and sends @ref kStatus_LPUART_RxIdle to LPUART callback.
58  *
59  * @param handle The EDMA handle.
60  * @param param Callback function parameter.
61  */
62 static void LPUART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
63 
64 /*!
65  * @brief Get the LPUART instance from peripheral base address.
66  *
67  * @param base LPUART peripheral base address.
68  * @return LPUART instance.
69  */
70 extern uint32_t LPUART_GetInstance(LPUART_Type *base);
71 
72 /*******************************************************************************
73  * Code
74  ******************************************************************************/
75 
LPUART_SendEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)76 static void LPUART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
77 {
78     assert(param);
79 
80     lpuart_edma_private_handle_t *lpuartPrivateHandle = (lpuart_edma_private_handle_t *)param;
81 
82     /* Avoid the warning for unused variables. */
83     handle = handle;
84     tcds = tcds;
85 
86     if (transferDone)
87     {
88         LPUART_TransferAbortSendEDMA(lpuartPrivateHandle->base, lpuartPrivateHandle->handle);
89 
90         if (lpuartPrivateHandle->handle->callback)
91         {
92             lpuartPrivateHandle->handle->callback(lpuartPrivateHandle->base, lpuartPrivateHandle->handle,
93                                                   kStatus_LPUART_TxIdle, lpuartPrivateHandle->handle->userData);
94         }
95     }
96 }
97 
LPUART_ReceiveEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)98 static void LPUART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
99 {
100     assert(param);
101 
102     lpuart_edma_private_handle_t *lpuartPrivateHandle = (lpuart_edma_private_handle_t *)param;
103 
104     /* Avoid warning for unused parameters. */
105     handle = handle;
106     tcds = tcds;
107 
108     if (transferDone)
109     {
110         /* Disable transfer. */
111         LPUART_TransferAbortReceiveEDMA(lpuartPrivateHandle->base, lpuartPrivateHandle->handle);
112 
113         if (lpuartPrivateHandle->handle->callback)
114         {
115             lpuartPrivateHandle->handle->callback(lpuartPrivateHandle->base, lpuartPrivateHandle->handle,
116                                                   kStatus_LPUART_RxIdle, lpuartPrivateHandle->handle->userData);
117         }
118     }
119 }
120 
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)121 void LPUART_TransferCreateHandleEDMA(LPUART_Type *base,
122                                      lpuart_edma_handle_t *handle,
123                                      lpuart_edma_transfer_callback_t callback,
124                                      void *userData,
125                                      edma_handle_t *txEdmaHandle,
126                                      edma_handle_t *rxEdmaHandle)
127 {
128     assert(handle);
129 
130     uint32_t instance = LPUART_GetInstance(base);
131 
132     s_edmaPrivateHandle[instance].base = base;
133     s_edmaPrivateHandle[instance].handle = handle;
134 
135     memset(handle, 0, sizeof(*handle));
136 
137     handle->rxState = kLPUART_RxIdle;
138     handle->txState = kLPUART_TxIdle;
139 
140     handle->rxEdmaHandle = rxEdmaHandle;
141     handle->txEdmaHandle = txEdmaHandle;
142 
143     handle->callback = callback;
144     handle->userData = userData;
145 
146 #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO
147     /* Note:
148        Take care of the RX FIFO, EDMA request only assert when received bytes
149        equal or more than RX water mark, there is potential issue if RX water
150        mark larger than 1.
151        For example, if RX FIFO water mark is 2, upper layer needs 5 bytes and
152        5 bytes are received. the last byte will be saved in FIFO but not trigger
153        EDMA transfer because the water mark is 2.
154      */
155     if (rxEdmaHandle)
156     {
157         base->WATER &= (~LPUART_WATER_RXWATER_MASK);
158     }
159 #endif
160 
161     /* Configure TX. */
162     if (txEdmaHandle)
163     {
164         EDMA_SetCallback(handle->txEdmaHandle, LPUART_SendEDMACallback, &s_edmaPrivateHandle[instance]);
165     }
166 
167     /* Configure RX. */
168     if (rxEdmaHandle)
169     {
170         EDMA_SetCallback(handle->rxEdmaHandle, LPUART_ReceiveEDMACallback, &s_edmaPrivateHandle[instance]);
171     }
172 }
173 
LPUART_SendEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,lpuart_transfer_t * xfer)174 status_t LPUART_SendEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, lpuart_transfer_t *xfer)
175 {
176     assert(handle);
177     assert(handle->txEdmaHandle);
178     assert(xfer);
179     assert(xfer->data);
180     assert(xfer->dataSize);
181 
182     edma_transfer_config_t xferConfig;
183     status_t status;
184 
185     /* If previous TX not finished. */
186     if (kLPUART_TxBusy == handle->txState)
187     {
188         status = kStatus_LPUART_TxBusy;
189     }
190     else
191     {
192         handle->txState = kLPUART_TxBusy;
193         handle->txDataSizeAll = xfer->dataSize;
194 
195         /* Prepare transfer. */
196         EDMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t), (void *)LPUART_GetDataRegisterAddress(base),
197                              sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_MemoryToPeripheral);
198 
199         /* Store the initially configured eDMA minor byte transfer count into the LPUART handle */
200         handle->nbytes = sizeof(uint8_t);
201 
202         /* Submit transfer. */
203         EDMA_SubmitTransfer(handle->txEdmaHandle, &xferConfig);
204         EDMA_StartTransfer(handle->txEdmaHandle);
205 
206         /* Enable LPUART TX EDMA. */
207         LPUART_EnableTxDMA(base, true);
208 
209         status = kStatus_Success;
210     }
211 
212     return status;
213 }
214 
LPUART_ReceiveEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,lpuart_transfer_t * xfer)215 status_t LPUART_ReceiveEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, lpuart_transfer_t *xfer)
216 {
217     assert(handle);
218     assert(handle->rxEdmaHandle);
219     assert(xfer);
220     assert(xfer->data);
221     assert(xfer->dataSize);
222 
223     edma_transfer_config_t xferConfig;
224     status_t status;
225 
226     /* If previous RX not finished. */
227     if (kLPUART_RxBusy == handle->rxState)
228     {
229         status = kStatus_LPUART_RxBusy;
230     }
231     else
232     {
233         handle->rxState = kLPUART_RxBusy;
234         handle->rxDataSizeAll = xfer->dataSize;
235 
236         /* Prepare transfer. */
237         EDMA_PrepareTransfer(&xferConfig, (void *)LPUART_GetDataRegisterAddress(base), sizeof(uint8_t), xfer->data,
238                              sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_PeripheralToMemory);
239 
240         /* Store the initially configured eDMA minor byte transfer count into the LPUART handle */
241         handle->nbytes = sizeof(uint8_t);
242 
243         /* Submit transfer. */
244         EDMA_SubmitTransfer(handle->rxEdmaHandle, &xferConfig);
245         EDMA_StartTransfer(handle->rxEdmaHandle);
246 
247         /* Enable LPUART RX EDMA. */
248         LPUART_EnableRxDMA(base, true);
249 
250         status = kStatus_Success;
251     }
252 
253     return status;
254 }
255 
LPUART_TransferAbortSendEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle)256 void LPUART_TransferAbortSendEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle)
257 {
258     assert(handle);
259     assert(handle->txEdmaHandle);
260 
261     /* Disable LPUART TX EDMA. */
262     LPUART_EnableTxDMA(base, false);
263 
264     /* Stop transfer. */
265     EDMA_AbortTransfer(handle->txEdmaHandle);
266 
267     handle->txState = kLPUART_TxIdle;
268 }
269 
LPUART_TransferAbortReceiveEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle)270 void LPUART_TransferAbortReceiveEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle)
271 {
272     assert(handle);
273     assert(handle->rxEdmaHandle);
274 
275     /* Disable LPUART RX EDMA. */
276     LPUART_EnableRxDMA(base, false);
277 
278     /* Stop transfer. */
279     EDMA_AbortTransfer(handle->rxEdmaHandle);
280 
281     handle->rxState = kLPUART_RxIdle;
282 }
283 
LPUART_TransferGetReceiveCountEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,uint32_t * count)284 status_t LPUART_TransferGetReceiveCountEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, uint32_t *count)
285 {
286     assert(handle);
287     assert(handle->rxEdmaHandle);
288     assert(count);
289 
290     if (kLPUART_RxIdle == handle->rxState)
291     {
292         return kStatus_NoTransferInProgress;
293     }
294 
295     *count = handle->rxDataSizeAll -
296              (uint32_t)handle->nbytes *
297                  EDMA_GetRemainingMajorLoopCount(handle->rxEdmaHandle->base, handle->rxEdmaHandle->channel);
298 
299     return kStatus_Success;
300 }
301 
LPUART_TransferGetSendCountEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,uint32_t * count)302 status_t LPUART_TransferGetSendCountEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, uint32_t *count)
303 {
304     assert(handle);
305     assert(handle->txEdmaHandle);
306     assert(count);
307 
308     if (kLPUART_TxIdle == handle->txState)
309     {
310         return kStatus_NoTransferInProgress;
311     }
312 
313     *count = handle->txDataSizeAll -
314              (uint32_t)handle->nbytes *
315                  EDMA_GetRemainingMajorLoopCount(handle->txEdmaHandle->base, handle->txEdmaHandle->channel);
316 
317     return kStatus_Success;
318 }
319