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