1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2017 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 /*<! Structure definition for lpuart_edma_private_handle_t. The structure is private. */
16 typedef struct _lpuart_edma_private_handle
17 {
18 LPUART_Type *base;
19 lpuart_edma_handle_t *handle;
20 } lpuart_edma_private_handle_t;
21
22 /* LPUART EDMA transfer handle. */
23 enum _lpuart_edma_tansfer_states
24 {
25 kLPUART_TxIdle, /* TX idle. */
26 kLPUART_TxBusy, /* TX busy. */
27 kLPUART_RxIdle, /* RX idle. */
28 kLPUART_RxBusy /* RX busy. */
29 };
30
31 /*******************************************************************************
32 * Definitions
33 ******************************************************************************/
34
35 /*<! Private handle only used for internally. */
36 static lpuart_edma_private_handle_t s_edmaPrivateHandle[FSL_FEATURE_SOC_LPUART_COUNT];
37
38 /*******************************************************************************
39 * Prototypes
40 ******************************************************************************/
41
42 /*!
43 * @brief LPUART EDMA send finished callback function.
44 *
45 * This function is called when LPUART EDMA send finished. It disables the LPUART
46 * TX EDMA request and sends @ref kStatus_LPUART_TxIdle to LPUART callback.
47 *
48 * @param handle The EDMA handle.
49 * @param param Callback function parameter.
50 */
51 static void LPUART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
52
53 /*!
54 * @brief LPUART EDMA receive finished callback function.
55 *
56 * This function is called when LPUART EDMA receive finished. It disables the LPUART
57 * RX EDMA request and sends @ref kStatus_LPUART_RxIdle to LPUART callback.
58 *
59 * @param handle The EDMA handle.
60 * @param param Callback function parameter.
61 */
62 static void LPUART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
63
64 /*!
65 * @brief Get the LPUART instance from peripheral base address.
66 *
67 * @param base LPUART peripheral base address.
68 * @return LPUART instance.
69 */
70 extern uint32_t LPUART_GetInstance(LPUART_Type *base);
71
72 /*******************************************************************************
73 * Code
74 ******************************************************************************/
75
LPUART_SendEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)76 static void LPUART_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
77 {
78 assert(param);
79
80 lpuart_edma_private_handle_t *lpuartPrivateHandle = (lpuart_edma_private_handle_t *)param;
81
82 /* Avoid the warning for unused variables. */
83 handle = handle;
84 tcds = tcds;
85
86 if (transferDone)
87 {
88 LPUART_TransferAbortSendEDMA(lpuartPrivateHandle->base, lpuartPrivateHandle->handle);
89
90 if (lpuartPrivateHandle->handle->callback)
91 {
92 lpuartPrivateHandle->handle->callback(lpuartPrivateHandle->base, lpuartPrivateHandle->handle,
93 kStatus_LPUART_TxIdle, lpuartPrivateHandle->handle->userData);
94 }
95 }
96 }
97
LPUART_ReceiveEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)98 static void LPUART_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
99 {
100 assert(param);
101
102 lpuart_edma_private_handle_t *lpuartPrivateHandle = (lpuart_edma_private_handle_t *)param;
103
104 /* Avoid warning for unused parameters. */
105 handle = handle;
106 tcds = tcds;
107
108 if (transferDone)
109 {
110 /* Disable transfer. */
111 LPUART_TransferAbortReceiveEDMA(lpuartPrivateHandle->base, lpuartPrivateHandle->handle);
112
113 if (lpuartPrivateHandle->handle->callback)
114 {
115 lpuartPrivateHandle->handle->callback(lpuartPrivateHandle->base, lpuartPrivateHandle->handle,
116 kStatus_LPUART_RxIdle, lpuartPrivateHandle->handle->userData);
117 }
118 }
119 }
120
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)121 void LPUART_TransferCreateHandleEDMA(LPUART_Type *base,
122 lpuart_edma_handle_t *handle,
123 lpuart_edma_transfer_callback_t callback,
124 void *userData,
125 edma_handle_t *txEdmaHandle,
126 edma_handle_t *rxEdmaHandle)
127 {
128 assert(handle);
129
130 uint32_t instance = LPUART_GetInstance(base);
131
132 s_edmaPrivateHandle[instance].base = base;
133 s_edmaPrivateHandle[instance].handle = handle;
134
135 memset(handle, 0, sizeof(*handle));
136
137 handle->rxState = kLPUART_RxIdle;
138 handle->txState = kLPUART_TxIdle;
139
140 handle->rxEdmaHandle = rxEdmaHandle;
141 handle->txEdmaHandle = txEdmaHandle;
142
143 handle->callback = callback;
144 handle->userData = userData;
145
146 #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO
147 /* Note:
148 Take care of the RX FIFO, EDMA request only assert when received bytes
149 equal or more than RX water mark, there is potential issue if RX water
150 mark larger than 1.
151 For example, if RX FIFO water mark is 2, upper layer needs 5 bytes and
152 5 bytes are received. the last byte will be saved in FIFO but not trigger
153 EDMA transfer because the water mark is 2.
154 */
155 if (rxEdmaHandle)
156 {
157 base->WATER &= (~LPUART_WATER_RXWATER_MASK);
158 }
159 #endif
160
161 /* Configure TX. */
162 if (txEdmaHandle)
163 {
164 EDMA_SetCallback(handle->txEdmaHandle, LPUART_SendEDMACallback, &s_edmaPrivateHandle[instance]);
165 }
166
167 /* Configure RX. */
168 if (rxEdmaHandle)
169 {
170 EDMA_SetCallback(handle->rxEdmaHandle, LPUART_ReceiveEDMACallback, &s_edmaPrivateHandle[instance]);
171 }
172 }
173
LPUART_SendEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,lpuart_transfer_t * xfer)174 status_t LPUART_SendEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, lpuart_transfer_t *xfer)
175 {
176 assert(handle);
177 assert(handle->txEdmaHandle);
178 assert(xfer);
179 assert(xfer->data);
180 assert(xfer->dataSize);
181
182 edma_transfer_config_t xferConfig;
183 status_t status;
184
185 /* If previous TX not finished. */
186 if (kLPUART_TxBusy == handle->txState)
187 {
188 status = kStatus_LPUART_TxBusy;
189 }
190 else
191 {
192 handle->txState = kLPUART_TxBusy;
193 handle->txDataSizeAll = xfer->dataSize;
194
195 /* Prepare transfer. */
196 EDMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t), (void *)LPUART_GetDataRegisterAddress(base),
197 sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_MemoryToPeripheral);
198
199 /* Store the initially configured eDMA minor byte transfer count into the LPUART handle */
200 handle->nbytes = sizeof(uint8_t);
201
202 /* Submit transfer. */
203 EDMA_SubmitTransfer(handle->txEdmaHandle, &xferConfig);
204 EDMA_StartTransfer(handle->txEdmaHandle);
205
206 /* Enable LPUART TX EDMA. */
207 LPUART_EnableTxDMA(base, true);
208
209 status = kStatus_Success;
210 }
211
212 return status;
213 }
214
LPUART_ReceiveEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,lpuart_transfer_t * xfer)215 status_t LPUART_ReceiveEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, lpuart_transfer_t *xfer)
216 {
217 assert(handle);
218 assert(handle->rxEdmaHandle);
219 assert(xfer);
220 assert(xfer->data);
221 assert(xfer->dataSize);
222
223 edma_transfer_config_t xferConfig;
224 status_t status;
225
226 /* If previous RX not finished. */
227 if (kLPUART_RxBusy == handle->rxState)
228 {
229 status = kStatus_LPUART_RxBusy;
230 }
231 else
232 {
233 handle->rxState = kLPUART_RxBusy;
234 handle->rxDataSizeAll = xfer->dataSize;
235
236 /* Prepare transfer. */
237 EDMA_PrepareTransfer(&xferConfig, (void *)LPUART_GetDataRegisterAddress(base), sizeof(uint8_t), xfer->data,
238 sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, kEDMA_PeripheralToMemory);
239
240 /* Store the initially configured eDMA minor byte transfer count into the LPUART handle */
241 handle->nbytes = sizeof(uint8_t);
242
243 /* Submit transfer. */
244 EDMA_SubmitTransfer(handle->rxEdmaHandle, &xferConfig);
245 EDMA_StartTransfer(handle->rxEdmaHandle);
246
247 /* Enable LPUART RX EDMA. */
248 LPUART_EnableRxDMA(base, true);
249
250 status = kStatus_Success;
251 }
252
253 return status;
254 }
255
LPUART_TransferAbortSendEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle)256 void LPUART_TransferAbortSendEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle)
257 {
258 assert(handle);
259 assert(handle->txEdmaHandle);
260
261 /* Disable LPUART TX EDMA. */
262 LPUART_EnableTxDMA(base, false);
263
264 /* Stop transfer. */
265 EDMA_AbortTransfer(handle->txEdmaHandle);
266
267 handle->txState = kLPUART_TxIdle;
268 }
269
LPUART_TransferAbortReceiveEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle)270 void LPUART_TransferAbortReceiveEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle)
271 {
272 assert(handle);
273 assert(handle->rxEdmaHandle);
274
275 /* Disable LPUART RX EDMA. */
276 LPUART_EnableRxDMA(base, false);
277
278 /* Stop transfer. */
279 EDMA_AbortTransfer(handle->rxEdmaHandle);
280
281 handle->rxState = kLPUART_RxIdle;
282 }
283
LPUART_TransferGetReceiveCountEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,uint32_t * count)284 status_t LPUART_TransferGetReceiveCountEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, uint32_t *count)
285 {
286 assert(handle);
287 assert(handle->rxEdmaHandle);
288 assert(count);
289
290 if (kLPUART_RxIdle == handle->rxState)
291 {
292 return kStatus_NoTransferInProgress;
293 }
294
295 *count = handle->rxDataSizeAll -
296 (uint32_t)handle->nbytes *
297 EDMA_GetRemainingMajorLoopCount(handle->rxEdmaHandle->base, handle->rxEdmaHandle->channel);
298
299 return kStatus_Success;
300 }
301
LPUART_TransferGetSendCountEDMA(LPUART_Type * base,lpuart_edma_handle_t * handle,uint32_t * count)302 status_t LPUART_TransferGetSendCountEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, uint32_t *count)
303 {
304 assert(handle);
305 assert(handle->txEdmaHandle);
306 assert(count);
307
308 if (kLPUART_TxIdle == handle->txState)
309 {
310 return kStatus_NoTransferInProgress;
311 }
312
313 *count = handle->txDataSizeAll -
314 (uint32_t)handle->nbytes *
315 EDMA_GetRemainingMajorLoopCount(handle->txEdmaHandle->base, handle->txEdmaHandle->channel);
316
317 return kStatus_Success;
318 }
319