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