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_flexio_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.flexio_uart_edma"
18 #endif
19 
20 /*<! Structure definition for uart_edma_private_handle_t. The structure is private. */
21 typedef struct _flexio_uart_edma_private_handle
22 {
23     FLEXIO_UART_Type *base;
24     flexio_uart_edma_handle_t *handle;
25 } flexio_uart_edma_private_handle_t;
26 
27 /* UART EDMA transfer handle. */
28 enum _flexio_uart_edma_tansfer_states
29 {
30     kFLEXIO_UART_TxIdle, /* TX idle. */
31     kFLEXIO_UART_TxBusy, /* TX busy. */
32     kFLEXIO_UART_RxIdle, /* RX idle. */
33     kFLEXIO_UART_RxBusy  /* RX busy. */
34 };
35 
36 /*******************************************************************************
37  * Variables
38  ******************************************************************************/
39 
40 /*< @brief user configurable flexio uart handle count. */
41 #define FLEXIO_UART_HANDLE_COUNT 2
42 
43 /*<! Private handle only used for internally. */
44 static flexio_uart_edma_private_handle_t s_edmaPrivateHandle[FLEXIO_UART_HANDLE_COUNT];
45 
46 /*******************************************************************************
47  * Prototypes
48  ******************************************************************************/
49 
50 /*!
51  * @brief FLEXIO UART EDMA send finished callback function.
52  *
53  * This function is called when FLEXIO UART EDMA send finished. It disables the UART
54  * TX EDMA request and sends @ref kStatus_FLEXIO_UART_TxIdle to FLEXIO UART callback.
55  *
56  * @param handle The EDMA handle.
57  * @param param Callback function parameter.
58  */
59 static void FLEXIO_UART_TransferSendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
60 
61 /*!
62  * @brief FLEXIO UART EDMA receive finished callback function.
63  *
64  * This function is called when FLEXIO UART EDMA receive finished. It disables the UART
65  * RX EDMA request and sends @ref kStatus_FLEXIO_UART_RxIdle to UART callback.
66  *
67  * @param handle The EDMA handle.
68  * @param param Callback function parameter.
69  */
70 static void FLEXIO_UART_TransferReceiveEDMACallback(edma_handle_t *handle,
71                                                     void *param,
72                                                     bool transferDone,
73                                                     uint32_t tcds);
74 
75 /*******************************************************************************
76  * Code
77  ******************************************************************************/
78 
FLEXIO_UART_TransferSendEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)79 static void FLEXIO_UART_TransferSendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
80 {
81     flexio_uart_edma_private_handle_t *uartPrivateHandle = (flexio_uart_edma_private_handle_t *)param;
82 
83     assert(uartPrivateHandle->handle != NULL);
84 
85     /* Avoid the warning for unused variables. */
86     handle = handle;
87     tcds   = tcds;
88 
89     if (transferDone)
90     {
91         FLEXIO_UART_TransferAbortSendEDMA(uartPrivateHandle->base, uartPrivateHandle->handle);
92 
93         if (uartPrivateHandle->handle->callback != NULL)
94         {
95             uartPrivateHandle->handle->callback(uartPrivateHandle->base, uartPrivateHandle->handle,
96                                                 kStatus_FLEXIO_UART_TxIdle, uartPrivateHandle->handle->userData);
97         }
98     }
99 }
100 
FLEXIO_UART_TransferReceiveEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)101 static void FLEXIO_UART_TransferReceiveEDMACallback(edma_handle_t *handle,
102                                                     void *param,
103                                                     bool transferDone,
104                                                     uint32_t tcds)
105 {
106     flexio_uart_edma_private_handle_t *uartPrivateHandle = (flexio_uart_edma_private_handle_t *)param;
107 
108     assert(uartPrivateHandle->handle != NULL);
109 
110     /* Avoid the warning for unused variables. */
111     handle = handle;
112     tcds   = tcds;
113 
114     if (transferDone)
115     {
116         /* Disable transfer. */
117         FLEXIO_UART_TransferAbortReceiveEDMA(uartPrivateHandle->base, uartPrivateHandle->handle);
118 
119         if (uartPrivateHandle->handle->callback != NULL)
120         {
121             uartPrivateHandle->handle->callback(uartPrivateHandle->base, uartPrivateHandle->handle,
122                                                 kStatus_FLEXIO_UART_RxIdle, uartPrivateHandle->handle->userData);
123         }
124     }
125 }
126 
127 /*!
128  * brief Initializes the UART handle which is used in transactional functions.
129  *
130  * param base Pointer to FLEXIO_UART_Type.
131  * param handle Pointer to flexio_uart_edma_handle_t structure.
132  * param callback The callback function.
133  * param userData The parameter of the callback function.
134  * param rxEdmaHandle User requested DMA handle for RX DMA transfer.
135  * param txEdmaHandle User requested DMA handle for TX DMA transfer.
136  * retval kStatus_Success Successfully create the handle.
137  * retval kStatus_OutOfRange The FlexIO SPI eDMA type/handle table out of range.
138  */
FLEXIO_UART_TransferCreateHandleEDMA(FLEXIO_UART_Type * base,flexio_uart_edma_handle_t * handle,flexio_uart_edma_transfer_callback_t callback,void * userData,edma_handle_t * txEdmaHandle,edma_handle_t * rxEdmaHandle)139 status_t FLEXIO_UART_TransferCreateHandleEDMA(FLEXIO_UART_Type *base,
140                                               flexio_uart_edma_handle_t *handle,
141                                               flexio_uart_edma_transfer_callback_t callback,
142                                               void *userData,
143                                               edma_handle_t *txEdmaHandle,
144                                               edma_handle_t *rxEdmaHandle)
145 {
146     assert(handle != NULL);
147 
148     uint8_t index = 0U;
149 
150     /* Find the an empty handle pointer to store the handle. */
151     for (index = 0U; index < (uint8_t)FLEXIO_UART_HANDLE_COUNT; index++)
152     {
153         if (s_edmaPrivateHandle[index].base == NULL)
154         {
155             s_edmaPrivateHandle[index].base   = base;
156             s_edmaPrivateHandle[index].handle = handle;
157             break;
158         }
159     }
160 
161     if (index == (uint8_t)FLEXIO_UART_HANDLE_COUNT)
162     {
163         return kStatus_OutOfRange;
164     }
165 
166     (void)memset(handle, 0, sizeof(*handle));
167 
168     handle->rxState = (uint8_t)kFLEXIO_UART_RxIdle;
169     handle->txState = (uint8_t)kFLEXIO_UART_TxIdle;
170 
171     handle->rxEdmaHandle = rxEdmaHandle;
172     handle->txEdmaHandle = txEdmaHandle;
173 
174     handle->callback = callback;
175     handle->userData = userData;
176 
177     /* Configure TX. */
178     if (txEdmaHandle != NULL)
179     {
180         EDMA_SetCallback(handle->txEdmaHandle, FLEXIO_UART_TransferSendEDMACallback, &s_edmaPrivateHandle);
181     }
182 
183     /* Configure RX. */
184     if (rxEdmaHandle != NULL)
185     {
186         EDMA_SetCallback(handle->rxEdmaHandle, FLEXIO_UART_TransferReceiveEDMACallback, &s_edmaPrivateHandle);
187     }
188 
189     return kStatus_Success;
190 }
191 
192 /*!
193  * brief Sends data using eDMA.
194  *
195  * This function sends data using eDMA. This is a non-blocking function, which returns
196  * right away. When all data is sent out, the send callback function is called.
197  *
198  * param base Pointer to FLEXIO_UART_Type
199  * param handle UART handle pointer.
200  * param xfer UART eDMA transfer structure, see #flexio_uart_transfer_t.
201  * retval kStatus_Success if succeed, others failed.
202  * retval kStatus_FLEXIO_UART_TxBusy Previous transfer on going.
203  */
FLEXIO_UART_TransferSendEDMA(FLEXIO_UART_Type * base,flexio_uart_edma_handle_t * handle,flexio_uart_transfer_t * xfer)204 status_t FLEXIO_UART_TransferSendEDMA(FLEXIO_UART_Type *base,
205                                       flexio_uart_edma_handle_t *handle,
206                                       flexio_uart_transfer_t *xfer)
207 {
208     assert(handle->txEdmaHandle != NULL);
209 
210     edma_transfer_config_t xferConfig;
211     status_t status;
212 
213     /* Return error if xfer invalid. */
214     if ((0U == xfer->dataSize) || (NULL == xfer->data))
215     {
216         return kStatus_InvalidArgument;
217     }
218 
219     /* If previous TX not finished. */
220     if ((uint8_t)kFLEXIO_UART_TxBusy == handle->txState)
221     {
222         status = kStatus_FLEXIO_UART_TxBusy;
223     }
224     else
225     {
226         handle->txState       = (uint8_t)kFLEXIO_UART_TxBusy;
227         handle->txDataSizeAll = xfer->dataSize;
228 
229         /* Prepare transfer. */
230         EDMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t),
231                              (uint32_t *)FLEXIO_UART_GetTxDataRegisterAddress(base), sizeof(uint8_t), sizeof(uint8_t),
232                              xfer->dataSize, kEDMA_MemoryToPeripheral);
233 
234         /* Store the initially configured eDMA minor byte transfer count into the FLEXIO UART handle */
235         handle->nbytes = 1U;
236 
237         /* Submit transfer. */
238         (void)EDMA_SubmitTransfer(handle->txEdmaHandle, &xferConfig);
239         EDMA_StartTransfer(handle->txEdmaHandle);
240 
241         /* Enable UART TX EDMA. */
242         FLEXIO_UART_EnableTxDMA(base, true);
243 
244         status = kStatus_Success;
245     }
246 
247     return status;
248 }
249 
250 /*!
251  * brief Receives data using eDMA.
252  *
253  * This function receives data using eDMA. This is a non-blocking function, which returns
254  * right away. When all data is received, the receive callback function is called.
255  *
256  * param base Pointer to FLEXIO_UART_Type
257  * param handle Pointer to flexio_uart_edma_handle_t structure
258  * param xfer UART eDMA transfer structure, see #flexio_uart_transfer_t.
259  * retval kStatus_Success if succeed, others failed.
260  * retval kStatus_UART_RxBusy Previous transfer on going.
261  */
FLEXIO_UART_TransferReceiveEDMA(FLEXIO_UART_Type * base,flexio_uart_edma_handle_t * handle,flexio_uart_transfer_t * xfer)262 status_t FLEXIO_UART_TransferReceiveEDMA(FLEXIO_UART_Type *base,
263                                          flexio_uart_edma_handle_t *handle,
264                                          flexio_uart_transfer_t *xfer)
265 {
266     assert(handle->rxEdmaHandle != NULL);
267 
268     edma_transfer_config_t xferConfig;
269     status_t status;
270 
271     /* Return error if xfer invalid. */
272     if ((0U == xfer->dataSize) || (NULL == xfer->data))
273     {
274         return kStatus_InvalidArgument;
275     }
276 
277     /* If previous RX not finished. */
278     if ((uint8_t)kFLEXIO_UART_RxBusy == handle->rxState)
279     {
280         status = kStatus_FLEXIO_UART_RxBusy;
281     }
282     else
283     {
284         handle->rxState       = (uint8_t)kFLEXIO_UART_RxBusy;
285         handle->rxDataSizeAll = xfer->dataSize;
286 
287         /* Prepare transfer. */
288         EDMA_PrepareTransfer(&xferConfig, (uint32_t *)FLEXIO_UART_GetRxDataRegisterAddress(base), sizeof(uint8_t),
289                              xfer->data, sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_PeripheralToMemory);
290 
291         /* Store the initially configured eDMA minor byte transfer count into the FLEXIO UART handle */
292         handle->nbytes = (uint8_t)sizeof(uint8_t);
293 
294         /* Submit transfer. */
295         (void)EDMA_SubmitTransfer(handle->rxEdmaHandle, &xferConfig);
296         EDMA_StartTransfer(handle->rxEdmaHandle);
297 
298         /* Enable UART RX EDMA. */
299         FLEXIO_UART_EnableRxDMA(base, true);
300 
301         status = kStatus_Success;
302     }
303 
304     return status;
305 }
306 
307 /*!
308  * brief Aborts the sent data which using eDMA.
309  *
310  * This function aborts sent data which using eDMA.
311  *
312  * param base Pointer to FLEXIO_UART_Type
313  * param handle Pointer to flexio_uart_edma_handle_t structure
314  */
FLEXIO_UART_TransferAbortSendEDMA(FLEXIO_UART_Type * base,flexio_uart_edma_handle_t * handle)315 void FLEXIO_UART_TransferAbortSendEDMA(FLEXIO_UART_Type *base, flexio_uart_edma_handle_t *handle)
316 {
317     assert(handle->txEdmaHandle != NULL);
318 
319     /* Disable UART TX EDMA. */
320     FLEXIO_UART_EnableTxDMA(base, false);
321 
322     /* Stop transfer. */
323     EDMA_StopTransfer(handle->txEdmaHandle);
324 
325     handle->txState = (uint8_t)kFLEXIO_UART_TxIdle;
326 }
327 
328 /*!
329  * brief Aborts the receive data which using eDMA.
330  *
331  * This function aborts the receive data which using eDMA.
332  *
333  * param base Pointer to FLEXIO_UART_Type
334  * param handle Pointer to flexio_uart_edma_handle_t structure
335  */
FLEXIO_UART_TransferAbortReceiveEDMA(FLEXIO_UART_Type * base,flexio_uart_edma_handle_t * handle)336 void FLEXIO_UART_TransferAbortReceiveEDMA(FLEXIO_UART_Type *base, flexio_uart_edma_handle_t *handle)
337 {
338     assert(handle->rxEdmaHandle != NULL);
339 
340     /* Disable UART RX EDMA. */
341     FLEXIO_UART_EnableRxDMA(base, false);
342 
343     /* Stop transfer. */
344     EDMA_StopTransfer(handle->rxEdmaHandle);
345 
346     handle->rxState = (uint8_t)kFLEXIO_UART_RxIdle;
347 }
348 
349 /*!
350  * brief Gets the number of bytes received.
351  *
352  * This function gets the number of bytes received.
353  *
354  * param base Pointer to FLEXIO_UART_Type
355  * param handle Pointer to flexio_uart_edma_handle_t structure
356  * param count Number of bytes received so far by the non-blocking transaction.
357  * retval kStatus_NoTransferInProgress transfer has finished or no transfer in progress.
358  * retval kStatus_Success Successfully return the count.
359  */
FLEXIO_UART_TransferGetReceiveCountEDMA(FLEXIO_UART_Type * base,flexio_uart_edma_handle_t * handle,size_t * count)360 status_t FLEXIO_UART_TransferGetReceiveCountEDMA(FLEXIO_UART_Type *base,
361                                                  flexio_uart_edma_handle_t *handle,
362                                                  size_t *count)
363 {
364     assert(handle != NULL);
365     assert(handle->rxEdmaHandle != NULL);
366     assert(count != NULL);
367 
368     if ((uint8_t)kFLEXIO_UART_RxIdle == handle->rxState)
369     {
370         return kStatus_NoTransferInProgress;
371     }
372 
373     *count = handle->rxDataSizeAll -
374              (uint32_t)handle->nbytes *
375                  EDMA_GetRemainingMajorLoopCount(handle->rxEdmaHandle->base, handle->rxEdmaHandle->channel);
376 
377     return kStatus_Success;
378 }
379 
380 /*!
381  * brief Gets the number of bytes sent out.
382  *
383  * This function gets the number of bytes sent out.
384  *
385  * param base Pointer to FLEXIO_UART_Type
386  * param handle Pointer to flexio_uart_edma_handle_t structure
387  * param count Number of bytes sent so far by the non-blocking transaction.
388  * retval kStatus_NoTransferInProgress transfer has finished or no transfer in progress.
389  * retval kStatus_Success Successfully return the count.
390  */
FLEXIO_UART_TransferGetSendCountEDMA(FLEXIO_UART_Type * base,flexio_uart_edma_handle_t * handle,size_t * count)391 status_t FLEXIO_UART_TransferGetSendCountEDMA(FLEXIO_UART_Type *base, flexio_uart_edma_handle_t *handle, size_t *count)
392 {
393     assert(handle != NULL);
394     assert(handle->txEdmaHandle != NULL);
395     assert(count != NULL);
396 
397     if ((uint8_t)kFLEXIO_UART_TxIdle == handle->txState)
398     {
399         return kStatus_NoTransferInProgress;
400     }
401 
402     *count = handle->txDataSizeAll -
403              (uint32_t)handle->nbytes *
404                  EDMA_GetRemainingMajorLoopCount(handle->txEdmaHandle->base, handle->txEdmaHandle->channel);
405 
406     return kStatus_Success;
407 }
408