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. */ |
21 | typedef 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. */ |
28 | enum |
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. */ |
80 | static 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 | */ |
95 | static 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 | */ |
106 | static void LPUART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds); |
107 | |
108 | /******************************************************************************* |
109 | * Code |
110 | ******************************************************************************/ |
111 | |
112 | static 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 | |
135 | static 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 | */ |
170 | void 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 | */ |
249 | status_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 | */ |
308 | status_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 | */ |
361 | void 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 | */ |
383 | void 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 | */ |
409 | status_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 | */ |
440 | status_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 | */ |
469 | void 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 | |