1 /*
2 * Copyright (c) 2016, 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_usart.h"
10 #include "fsl_device_registers.h"
11 #include "fsl_dma.h"
12 #include "fsl_flexcomm.h"
13 #include "fsl_usart_dma.h"
14
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.flexcomm_usart_dma"
18 #endif
19
20 /*******************************************************************************
21 * Definitions
22 ******************************************************************************/
23 enum
24 {
25 kUSART_TxIdle, /* TX idle. */
26 kUSART_TxBusy, /* TX busy. */
27 kUSART_RxIdle, /* RX idle. */
28 kUSART_RxBusy /* RX busy. */
29 };
30
31 /*! @brief Typedef for usart DMA interrupt handler. */
32 typedef void (*flexcomm_usart_dma_irq_handler_t)(USART_Type *base, usart_dma_handle_t *handle);
33
34 /*<! Structure definition for uart_dma_handle_t. The structure is private. */
35 typedef struct _usart_dma_private_handle
36 {
37 USART_Type *base;
38 usart_dma_handle_t *handle;
39 } usart_dma_private_handle_t;
40
41 /*!
42 * @brief Used for conversion from `flexcomm_usart_irq_handler_t` to `flexcomm_irq_handler_t`
43 */
44 typedef union usart_dma_to_flexcomm
45 {
46 flexcomm_usart_dma_irq_handler_t usart_dma_handler;
47 flexcomm_irq_handler_t flexcomm_handler;
48 } usart_dma_to_flexcomm_t;
49
50 /*******************************************************************************
51 * Variables
52 ******************************************************************************/
53 /*! @brief IRQ name array */
54 static const IRQn_Type s_usartIRQ[] = USART_IRQS;
55 /*<! Private handle only used for internally. */
56 static usart_dma_private_handle_t s_dmaPrivateHandle[FSL_FEATURE_SOC_USART_COUNT];
57
58 /*******************************************************************************
59 * Prototypes
60 ******************************************************************************/
61 /*!
62 * @brief USART DMA IRQ handle function.
63 *
64 * This function handles the USART transmit idle interrupt and invoke call back.
65 *
66 * @param base USART peripheral base address.
67 * @param handle USART handle pointer.
68 */
69 void USART_TransferDMAHandleIRQ(USART_Type *base, usart_dma_handle_t *handle);
70
71 /*******************************************************************************
72 * Code
73 ******************************************************************************/
74
USART_TransferSendDMACallback(dma_handle_t * handle,void * param,bool transferDone,uint32_t intmode)75 static void USART_TransferSendDMACallback(dma_handle_t *handle, void *param, bool transferDone, uint32_t intmode)
76 {
77 assert(handle != NULL);
78 assert(param != NULL);
79
80 usart_dma_private_handle_t *usartPrivateHandle = (usart_dma_private_handle_t *)param;
81
82 /* Disable UART TX DMA. */
83 USART_EnableTxDMA(usartPrivateHandle->base, false);
84
85 /* Enable tx idle interrupt */
86 usartPrivateHandle->base->INTENSET = USART_INTENSET_TXIDLEEN_MASK;
87 }
88
USART_TransferReceiveDMACallback(dma_handle_t * handle,void * param,bool transferDone,uint32_t intmode)89 static void USART_TransferReceiveDMACallback(dma_handle_t *handle, void *param, bool transferDone, uint32_t intmode)
90 {
91 assert(handle != NULL);
92 assert(param != NULL);
93
94 usart_dma_private_handle_t *usartPrivateHandle = (usart_dma_private_handle_t *)param;
95
96 /* Disable UART RX DMA. */
97 USART_EnableRxDMA(usartPrivateHandle->base, false);
98
99 usartPrivateHandle->handle->rxState = (uint8_t)kUSART_RxIdle;
100
101 if (usartPrivateHandle->handle->callback != NULL)
102 {
103 usartPrivateHandle->handle->callback(usartPrivateHandle->base, usartPrivateHandle->handle, kStatus_USART_RxIdle,
104 usartPrivateHandle->handle->userData);
105 }
106 }
107
108 /*!
109 * brief Initializes the USART handle which is used in transactional functions.
110 * param base USART peripheral base address.
111 * param handle Pointer to usart_dma_handle_t structure.
112 * param callback Callback function.
113 * param userData User data.
114 * param txDmaHandle User-requested DMA handle for TX DMA transfer.
115 * param rxDmaHandle User-requested DMA handle for RX DMA transfer.
116 */
USART_TransferCreateHandleDMA(USART_Type * base,usart_dma_handle_t * handle,usart_dma_transfer_callback_t callback,void * userData,dma_handle_t * txDmaHandle,dma_handle_t * rxDmaHandle)117 status_t USART_TransferCreateHandleDMA(USART_Type *base,
118 usart_dma_handle_t *handle,
119 usart_dma_transfer_callback_t callback,
120 void *userData,
121 dma_handle_t *txDmaHandle,
122 dma_handle_t *rxDmaHandle)
123 {
124 uint32_t instance = 0;
125
126 /* check 'base' */
127 assert(!(NULL == base));
128 if (NULL == base)
129 {
130 return kStatus_InvalidArgument;
131 }
132 /* check 'handle' */
133 assert(!(NULL == handle));
134 if (NULL == handle)
135 {
136 return kStatus_InvalidArgument;
137 }
138
139 instance = USART_GetInstance(base);
140
141 (void)memset(handle, 0, sizeof(*handle));
142 /* assign 'base' and 'handle' */
143 s_dmaPrivateHandle[instance].base = base;
144 s_dmaPrivateHandle[instance].handle = handle;
145
146 /* set tx/rx 'idle' state */
147 handle->rxState = (uint8_t)kUSART_RxIdle;
148 handle->txState = (uint8_t)kUSART_TxIdle;
149
150 handle->callback = callback;
151 handle->userData = userData;
152
153 handle->rxDmaHandle = rxDmaHandle;
154 handle->txDmaHandle = txDmaHandle;
155
156 /* Set USART_TransferDMAHandleIRQ as DMA IRQ handler */
157 usart_dma_to_flexcomm_t handler;
158 handler.usart_dma_handler = USART_TransferDMAHandleIRQ;
159 FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle);
160 /* Enable NVIC IRQ. */
161 (void)EnableIRQ(s_usartIRQ[instance]);
162
163 /* Configure TX. */
164 if (txDmaHandle != NULL)
165 {
166 DMA_SetCallback(txDmaHandle, USART_TransferSendDMACallback, &s_dmaPrivateHandle[instance]);
167 }
168
169 /* Configure RX. */
170 if (rxDmaHandle != NULL)
171 {
172 DMA_SetCallback(rxDmaHandle, USART_TransferReceiveDMACallback, &s_dmaPrivateHandle[instance]);
173 }
174
175 return kStatus_Success;
176 }
177
178 /*!
179 * brief Sends data using DMA.
180 *
181 * This function sends data using DMA. This is a non-blocking function, which returns
182 * right away. When all data is sent, the send callback function is called.
183 *
184 * param base USART peripheral base address.
185 * param handle USART handle pointer.
186 * param xfer USART DMA transfer structure. See #usart_transfer_t.
187 * retval kStatus_Success if succeed, others failed.
188 * retval kStatus_USART_TxBusy Previous transfer on going.
189 * retval kStatus_InvalidArgument Invalid argument.
190 */
USART_TransferSendDMA(USART_Type * base,usart_dma_handle_t * handle,usart_transfer_t * xfer)191 status_t USART_TransferSendDMA(USART_Type *base, usart_dma_handle_t *handle, usart_transfer_t *xfer)
192 {
193 assert(handle != NULL);
194 assert(handle->txDmaHandle != NULL);
195 assert(xfer != NULL);
196 assert(xfer->data != NULL);
197 assert(xfer->dataSize != 0U);
198
199 dma_transfer_config_t xferConfig;
200 status_t status;
201 uint32_t address = (uint32_t)&base->FIFOWR;
202
203 /* If previous TX not finished. */
204 if ((uint8_t)kUSART_TxBusy == handle->txState)
205 {
206 status = kStatus_USART_TxBusy;
207 }
208 else
209 {
210 handle->txState = (uint8_t)kUSART_TxBusy;
211 handle->txDataSizeAll = xfer->dataSize;
212
213 /* Enable DMA request from txFIFO */
214 USART_EnableTxDMA(base, true);
215
216 /* Prepare transfer. */
217 DMA_PrepareTransfer(&xferConfig, xfer->data, (uint32_t *)address, sizeof(uint8_t), xfer->dataSize,
218 kDMA_MemoryToPeripheral, NULL);
219
220 /* Submit transfer. */
221 (void)DMA_SubmitTransfer(handle->txDmaHandle, &xferConfig);
222 DMA_StartTransfer(handle->txDmaHandle);
223
224 status = kStatus_Success;
225 }
226
227 return status;
228 }
229
230 /*!
231 * brief Receives data using DMA.
232 *
233 * This function receives data using DMA. This is a non-blocking function, which returns
234 * right away. When all data is received, the receive callback function is called.
235 *
236 * param base USART peripheral base address.
237 * param handle Pointer to usart_dma_handle_t structure.
238 * param xfer USART DMA transfer structure. See #usart_transfer_t.
239 * retval kStatus_Success if succeed, others failed.
240 * retval kStatus_USART_RxBusy Previous transfer on going.
241 * retval kStatus_InvalidArgument Invalid argument.
242 */
USART_TransferReceiveDMA(USART_Type * base,usart_dma_handle_t * handle,usart_transfer_t * xfer)243 status_t USART_TransferReceiveDMA(USART_Type *base, usart_dma_handle_t *handle, usart_transfer_t *xfer)
244 {
245 assert(handle != NULL);
246 assert(handle->rxDmaHandle != NULL);
247 assert(xfer != NULL);
248 assert(xfer->data != NULL);
249 assert(xfer->dataSize != 0U);
250
251 dma_transfer_config_t xferConfig;
252 status_t status;
253 uint32_t address = (uint32_t)&base->FIFORD;
254
255 /* If previous RX not finished. */
256 if ((uint8_t)kUSART_RxBusy == handle->rxState)
257 {
258 status = kStatus_USART_RxBusy;
259 }
260 else
261 {
262 handle->rxState = (uint8_t)kUSART_RxBusy;
263 handle->rxDataSizeAll = xfer->dataSize;
264
265 /* Enable DMA request from rxFIFO */
266 USART_EnableRxDMA(base, true);
267
268 /* Prepare transfer. */
269 DMA_PrepareTransfer(&xferConfig, (uint32_t *)address, xfer->data, sizeof(uint8_t), xfer->dataSize,
270 kDMA_PeripheralToMemory, NULL);
271
272 /* Submit transfer. */
273 (void)DMA_SubmitTransfer(handle->rxDmaHandle, &xferConfig);
274 DMA_StartTransfer(handle->rxDmaHandle);
275
276 status = kStatus_Success;
277 }
278
279 return status;
280 }
281
282 /*!
283 * brief Aborts the sent data using DMA.
284 *
285 * This function aborts send data using DMA.
286 *
287 * param base USART peripheral base address
288 * param handle Pointer to usart_dma_handle_t structure
289 */
USART_TransferAbortSendDMA(USART_Type * base,usart_dma_handle_t * handle)290 void USART_TransferAbortSendDMA(USART_Type *base, usart_dma_handle_t *handle)
291 {
292 assert(NULL != handle);
293 assert(NULL != handle->txDmaHandle);
294
295 /* Stop transfer. */
296 DMA_AbortTransfer(handle->txDmaHandle);
297 handle->txState = (uint8_t)kUSART_TxIdle;
298 }
299
300 /*!
301 * brief Aborts the received data using DMA.
302 *
303 * This function aborts the received data using DMA.
304 *
305 * param base USART peripheral base address
306 * param handle Pointer to usart_dma_handle_t structure
307 */
USART_TransferAbortReceiveDMA(USART_Type * base,usart_dma_handle_t * handle)308 void USART_TransferAbortReceiveDMA(USART_Type *base, usart_dma_handle_t *handle)
309 {
310 assert(NULL != handle);
311 assert(NULL != handle->rxDmaHandle);
312
313 /* Stop transfer. */
314 DMA_AbortTransfer(handle->rxDmaHandle);
315 handle->rxState = (uint8_t)kUSART_RxIdle;
316 }
317
318 /*!
319 * brief Get the number of bytes that have been received.
320 *
321 * This function gets the number of bytes that have been received.
322 *
323 * param base USART peripheral base address.
324 * param handle USART handle pointer.
325 * param count Receive bytes count.
326 * retval kStatus_NoTransferInProgress No receive in progress.
327 * retval kStatus_InvalidArgument Parameter is invalid.
328 * retval kStatus_Success Get successfully through the parameter \p count;
329 */
USART_TransferGetReceiveCountDMA(USART_Type * base,usart_dma_handle_t * handle,uint32_t * count)330 status_t USART_TransferGetReceiveCountDMA(USART_Type *base, usart_dma_handle_t *handle, uint32_t *count)
331 {
332 assert(NULL != handle);
333 assert(NULL != handle->rxDmaHandle);
334 assert(NULL != count);
335
336 if ((uint8_t)kUSART_RxIdle == handle->rxState)
337 {
338 return kStatus_NoTransferInProgress;
339 }
340
341 *count = handle->rxDataSizeAll - DMA_GetRemainingBytes(handle->rxDmaHandle->base, handle->rxDmaHandle->channel);
342
343 return kStatus_Success;
344 }
345
346 /*!
347 * brief Get the number of bytes that have been sent.
348 *
349 * This function gets the number of bytes that have been sent.
350 *
351 * param base USART peripheral base address.
352 * param handle USART handle pointer.
353 * param count Sent bytes count.
354 * retval kStatus_NoTransferInProgress No receive in progress.
355 * retval kStatus_InvalidArgument Parameter is invalid.
356 * retval kStatus_Success Get successfully through the parameter \p count;
357 */
USART_TransferGetSendCountDMA(USART_Type * base,usart_dma_handle_t * handle,uint32_t * count)358 status_t USART_TransferGetSendCountDMA(USART_Type *base, usart_dma_handle_t *handle, uint32_t *count)
359 {
360 assert(NULL != handle);
361 assert(NULL != handle->txDmaHandle);
362 assert(NULL != count);
363
364 if ((uint8_t)kUSART_TxIdle == handle->txState)
365 {
366 return kStatus_NoTransferInProgress;
367 }
368
369 *count = handle->txDataSizeAll - DMA_GetRemainingBytes(handle->txDmaHandle->base, handle->txDmaHandle->channel);
370
371 return kStatus_Success;
372 }
373
USART_TransferDMAHandleIRQ(USART_Type * base,usart_dma_handle_t * handle)374 void USART_TransferDMAHandleIRQ(USART_Type *base, usart_dma_handle_t *handle)
375 {
376 /* Check arguments */
377 assert((NULL != base) && (NULL != handle));
378
379 /* Tx idle interrupt happens means that all the tx data have been sent out to bus, set the tx state to idle */
380 handle->txState = (uint8_t)kUSART_TxIdle;
381
382 /* Disable tx idle interrupt */
383 base->INTENCLR = USART_INTENCLR_TXIDLECLR_MASK;
384
385 /* Invoke callback */
386 if (handle->callback != NULL)
387 {
388 handle->callback(base, handle, kStatus_USART_TxIdle, handle->userData);
389 }
390 }
391