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