1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017 NXP
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * o Redistributions of source code must retain the above copyright notice, this list
9  *   of conditions and the following disclaimer.
10  *
11  * o Redistributions in binary form must reproduce the above copyright notice, this
12  *   list of conditions and the following disclaimer in the documentation and/or
13  *   other materials provided with the distribution.
14  *
15  * o Neither the name of the copyright holder nor the names of its
16  *   contributors may be used to endorse or promote products derived from this
17  *   software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "fsl_uart_edma.h"
32 #include "fsl_dmamux.h"
33 
34 /*******************************************************************************
35  * Definitions
36  ******************************************************************************/
37 
38 /* Array of UART handle. */
39 #if (defined(UART5))
40 #define UART_HANDLE_ARRAY_SIZE 6
41 #else /* UART5 */
42 #if (defined(UART4))
43 #define UART_HANDLE_ARRAY_SIZE 5
44 #else /* UART4 */
45 #if (defined(UART3))
46 #define UART_HANDLE_ARRAY_SIZE 4
47 #else /* UART3 */
48 #if (defined(UART2))
49 #define UART_HANDLE_ARRAY_SIZE 3
50 #else /* UART2 */
51 #if (defined(UART1))
52 #define UART_HANDLE_ARRAY_SIZE 2
53 #else /* UART1 */
54 #if (defined(UART0))
55 #define UART_HANDLE_ARRAY_SIZE 1
56 #else /* UART0 */
57 #error No UART instance.
58 #endif /* UART 0 */
59 #endif /* UART 1 */
60 #endif /* UART 2 */
61 #endif /* UART 3 */
62 #endif /* UART 4 */
63 #endif /* UART 5 */
64 
65 /*<! Structure definition for uart_edma_private_handle_t. The structure is private. */
66 typedef struct _uart_edma_private_handle
67 {
68     UART_Type *base;
69     uart_edma_handle_t *handle;
70 } uart_edma_private_handle_t;
71 
72 /* UART EDMA transfer handle. */
73 enum _uart_edma_tansfer_states
74 {
75     kUART_TxIdle, /* TX idle. */
76     kUART_TxBusy, /* TX busy. */
77     kUART_RxIdle, /* RX idle. */
78     kUART_RxBusy  /* RX busy. */
79 };
80 
81 /*******************************************************************************
82  * Definitions
83  ******************************************************************************/
84 
85 /*<! Private handle only used for internally. */
86 static uart_edma_private_handle_t s_edmaPrivateHandle[UART_HANDLE_ARRAY_SIZE];
87 
88 /*******************************************************************************
89  * Prototypes
90  ******************************************************************************/
91 
92 /*!
93  * @brief UART EDMA send finished callback function.
94  *
95  * This function is called when UART EDMA send finished. It disables the UART
96  * TX EDMA request and sends @ref kStatus_UART_TxIdle to UART callback.
97  *
98  * @param handle The EDMA handle.
99  * @param param Callback function parameter.
100  */
101 static void UART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
102 
103 /*!
104  * @brief UART EDMA receive finished callback function.
105  *
106  * This function is called when UART EDMA receive finished. It disables the UART
107  * RX EDMA request and sends @ref kStatus_UART_RxIdle to UART callback.
108  *
109  * @param handle The EDMA handle.
110  * @param param Callback function parameter.
111  */
112 static void UART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
113 
114 /*!
115  * @brief Get the UART instance from peripheral base address.
116  *
117  * @param base UART peripheral base address.
118  * @return UART instance.
119  */
120 extern uint32_t UART_GetInstance(UART_Type *base);
121 
122 /*******************************************************************************
123  * Code
124  ******************************************************************************/
125 
UART_SendEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)126 static void UART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
127 {
128     assert(param);
129 
130     uart_edma_private_handle_t *uartPrivateHandle = (uart_edma_private_handle_t *)param;
131 
132     /* Avoid the warning for unused variables. */
133     handle = handle;
134     tcds = tcds;
135 
136     if (transferDone)
137     {
138         UART_TransferAbortSendEDMA(uartPrivateHandle->base, uartPrivateHandle->handle);
139 
140         if (uartPrivateHandle->handle->callback)
141         {
142             uartPrivateHandle->handle->callback(uartPrivateHandle->base, uartPrivateHandle->handle, kStatus_UART_TxIdle,
143                                                 uartPrivateHandle->handle->userData);
144         }
145     }
146 }
147 
UART_ReceiveEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)148 static void UART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
149 {
150     assert(param);
151 
152     uart_edma_private_handle_t *uartPrivateHandle = (uart_edma_private_handle_t *)param;
153 
154     /* Avoid warning for unused parameters. */
155     handle = handle;
156     tcds = tcds;
157 
158     if (transferDone)
159     {
160         /* Disable transfer. */
161         UART_TransferAbortReceiveEDMA(uartPrivateHandle->base, uartPrivateHandle->handle);
162 
163         if (uartPrivateHandle->handle->callback)
164         {
165             uartPrivateHandle->handle->callback(uartPrivateHandle->base, uartPrivateHandle->handle, kStatus_UART_RxIdle,
166                                                 uartPrivateHandle->handle->userData);
167         }
168     }
169 }
170 
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)171 void UART_TransferCreateHandleEDMA(UART_Type *base,
172                                    uart_edma_handle_t *handle,
173                                    uart_edma_transfer_callback_t callback,
174                                    void *userData,
175                                    edma_handle_t *txEdmaHandle,
176                                    edma_handle_t *rxEdmaHandle)
177 {
178     assert(handle);
179 
180     uint32_t instance = UART_GetInstance(base);
181 
182     s_edmaPrivateHandle[instance].base = base;
183     s_edmaPrivateHandle[instance].handle = handle;
184 
185     memset(handle, 0, sizeof(*handle));
186 
187     handle->rxState = kUART_RxIdle;
188     handle->txState = kUART_TxIdle;
189 
190     handle->rxEdmaHandle = rxEdmaHandle;
191     handle->txEdmaHandle = txEdmaHandle;
192 
193     handle->callback = callback;
194     handle->userData = userData;
195 
196 #if defined(FSL_FEATURE_UART_HAS_FIFO) && FSL_FEATURE_UART_HAS_FIFO
197     /* Note:
198        Take care of the RX FIFO, EDMA request only assert when received bytes
199        equal or more than RX water mark, there is potential issue if RX water
200        mark larger than 1.
201        For example, if RX FIFO water mark is 2, upper layer needs 5 bytes and
202        5 bytes are received. the last byte will be saved in FIFO but not trigger
203        EDMA transfer because the water mark is 2.
204      */
205     if (rxEdmaHandle)
206     {
207         base->RWFIFO = 1U;
208     }
209 #endif
210 
211     /* Configure TX. */
212     if (txEdmaHandle)
213     {
214         EDMA_SetCallback(handle->txEdmaHandle, UART_SendEDMACallback, &s_edmaPrivateHandle[instance]);
215     }
216 
217     /* Configure RX. */
218     if (rxEdmaHandle)
219     {
220         EDMA_SetCallback(handle->rxEdmaHandle, UART_ReceiveEDMACallback, &s_edmaPrivateHandle[instance]);
221     }
222 }
223 
UART_SendEDMA(UART_Type * base,uart_edma_handle_t * handle,uart_transfer_t * xfer)224 status_t UART_SendEDMA(UART_Type *base, uart_edma_handle_t *handle, uart_transfer_t *xfer)
225 {
226     assert(handle);
227     assert(handle->txEdmaHandle);
228     assert(xfer);
229     assert(xfer->data);
230     assert(xfer->dataSize);
231 
232     edma_transfer_config_t xferConfig;
233     status_t status;
234 
235     /* If previous TX not finished. */
236     if (kUART_TxBusy == handle->txState)
237     {
238         status = kStatus_UART_TxBusy;
239     }
240     else
241     {
242         handle->txState = kUART_TxBusy;
243         handle->txDataSizeAll = xfer->dataSize;
244 
245         /* Prepare transfer. */
246         EDMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t), (void *)UART_GetDataRegisterAddress(base),
247                              sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_MemoryToPeripheral);
248 
249         /* Store the initially configured eDMA minor byte transfer count into the UART handle */
250         handle->nbytes = sizeof(uint8_t);
251 
252         /* Submit transfer. */
253         EDMA_SubmitTransfer(handle->txEdmaHandle, &xferConfig);
254         EDMA_StartTransfer(handle->txEdmaHandle);
255 
256         /* Enable UART TX EDMA. */
257         UART_EnableTxDMA(base, true);
258 
259         status = kStatus_Success;
260     }
261 
262     return status;
263 }
264 
UART_ReceiveEDMA(UART_Type * base,uart_edma_handle_t * handle,uart_transfer_t * xfer)265 status_t UART_ReceiveEDMA(UART_Type *base, uart_edma_handle_t *handle, uart_transfer_t *xfer)
266 {
267     assert(handle);
268     assert(handle->rxEdmaHandle);
269     assert(xfer);
270     assert(xfer->data);
271     assert(xfer->dataSize);
272 
273     edma_transfer_config_t xferConfig;
274     status_t status;
275 
276     /* If previous RX not finished. */
277     if (kUART_RxBusy == handle->rxState)
278     {
279         status = kStatus_UART_RxBusy;
280     }
281     else
282     {
283         handle->rxState = kUART_RxBusy;
284         handle->rxDataSizeAll = xfer->dataSize;
285 
286         /* Prepare transfer. */
287         EDMA_PrepareTransfer(&xferConfig, (void *)UART_GetDataRegisterAddress(base), sizeof(uint8_t), xfer->data,
288                              sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_PeripheralToMemory);
289 
290         /* Store the initially configured eDMA minor byte transfer count into the UART handle */
291         handle->nbytes = sizeof(uint8_t);
292 
293         /* Submit transfer. */
294         EDMA_SubmitTransfer(handle->rxEdmaHandle, &xferConfig);
295         EDMA_StartTransfer(handle->rxEdmaHandle);
296 
297         /* Enable UART RX EDMA. */
298         UART_EnableRxDMA(base, true);
299 
300         status = kStatus_Success;
301     }
302 
303     return status;
304 }
305 
UART_TransferAbortSendEDMA(UART_Type * base,uart_edma_handle_t * handle)306 void UART_TransferAbortSendEDMA(UART_Type *base, uart_edma_handle_t *handle)
307 {
308     assert(handle);
309     assert(handle->txEdmaHandle);
310 
311     /* Disable UART TX EDMA. */
312     UART_EnableTxDMA(base, false);
313 
314     /* Stop transfer. */
315     EDMA_AbortTransfer(handle->txEdmaHandle);
316 
317     handle->txState = kUART_TxIdle;
318 }
319 
UART_TransferAbortReceiveEDMA(UART_Type * base,uart_edma_handle_t * handle)320 void UART_TransferAbortReceiveEDMA(UART_Type *base, uart_edma_handle_t *handle)
321 {
322     assert(handle);
323     assert(handle->rxEdmaHandle);
324 
325     /* Disable UART RX EDMA. */
326     UART_EnableRxDMA(base, false);
327 
328     /* Stop transfer. */
329     EDMA_AbortTransfer(handle->rxEdmaHandle);
330 
331     handle->rxState = kUART_RxIdle;
332 }
333 
UART_TransferGetReceiveCountEDMA(UART_Type * base,uart_edma_handle_t * handle,uint32_t * count)334 status_t UART_TransferGetReceiveCountEDMA(UART_Type *base, uart_edma_handle_t *handle, uint32_t *count)
335 {
336     assert(handle);
337     assert(handle->rxEdmaHandle);
338     assert(count);
339 
340     if (kUART_RxIdle == handle->rxState)
341     {
342         return kStatus_NoTransferInProgress;
343     }
344 
345     *count = handle->rxDataSizeAll -
346              (uint32_t)handle->nbytes *
347                  EDMA_GetRemainingMajorLoopCount(handle->rxEdmaHandle->base, handle->rxEdmaHandle->channel);
348 
349     return kStatus_Success;
350 }
351 
UART_TransferGetSendCountEDMA(UART_Type * base,uart_edma_handle_t * handle,uint32_t * count)352 status_t UART_TransferGetSendCountEDMA(UART_Type *base, uart_edma_handle_t *handle, uint32_t *count)
353 {
354     assert(handle);
355     assert(handle->txEdmaHandle);
356     assert(count);
357 
358     if (kUART_TxIdle == handle->txState)
359     {
360         return kStatus_NoTransferInProgress;
361     }
362 
363     *count = handle->txDataSizeAll -
364              (uint32_t)handle->nbytes *
365                  EDMA_GetRemainingMajorLoopCount(handle->txEdmaHandle->base, handle->txEdmaHandle->channel);
366 
367     return kStatus_Success;
368 }
369