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