1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017 NXP
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * o Redistributions of source code must retain the above copyright notice, this list
9  *   of conditions and the following disclaimer.
10  *
11  * o Redistributions in binary form must reproduce the above copyright notice, this
12  *   list of conditions and the following disclaimer in the documentation and/or
13  *   other materials provided with the distribution.
14  *
15  * o Neither the name of the copyright holder nor the names of its
16  *   contributors may be used to endorse or promote products derived from this
17  *   software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "fsl_lpsci_dma.h"
32 #include "fsl_dmamux.h"
33 
34 /*******************************************************************************
35  * Definitions
36  ******************************************************************************/
37 
38 /*<! Structure definition for lpsci_dma_handle_t. The structure is private. */
39 typedef struct _lpsci_dma_private_handle
40 {
41     UART0_Type *base;
42     lpsci_dma_handle_t *handle;
43 } lpsci_dma_private_handle_t;
44 
45 /* LPSCI DMA transfer handle. */
46 enum _lpsci_dma_tansfer_states
47 {
48     kLPSCI_TxIdle, /* TX idle. */
49     kLPSCI_TxBusy, /* TX busy. */
50     kLPSCI_RxIdle, /* RX idle. */
51     kLPSCI_RxBusy  /* RX busy. */
52 };
53 
54 /*******************************************************************************
55  * Variables
56  ******************************************************************************/
57 
58 /*<! Private handle only used for internally. */
59 static lpsci_dma_private_handle_t s_dmaPrivateHandle[FSL_FEATURE_SOC_LPSCI_COUNT];
60 
61 /*******************************************************************************
62  * Prototypes
63  ******************************************************************************/
64 
65 /*!
66  * @brief LPSCI DMA send finished callback function.
67  *
68  * This function is called when LPSCI DMA send finished. It disables the LPSCI
69  * TX DMA request and sends @ref kStatus_LPSCI_TxIdle to LPSCI callback.
70  *
71  * @param handle The DMA handle.
72  * @param param Callback function parameter.
73  */
74 static void LPSCI_TransferSendDMACallback(dma_handle_t *handle, void *param);
75 
76 /*!
77  * @brief LPSCI DMA receive finished callback function.
78  *
79  * This function is called when LPSCI DMA receive finished. It disables the LPSCI
80  * RX DMA request and sends @ref kStatus_LPSCI_RxIdle to LPSCI callback.
81  *
82  * @param handle The DMA handle.
83  * @param param Callback function parameter.
84  */
85 static void LPSCI_TransferReceiveDMACallback(dma_handle_t *handle, void *param);
86 
87 /*!
88  * @brief Get the LPSCI instance from peripheral base address.
89  *
90  * @param base LPSCI peripheral base address.
91  * @return LPSCI instance.
92  */
93 extern uint32_t LPSCI_GetInstance(UART0_Type *base);
94 
95 /*******************************************************************************
96  * Code
97  ******************************************************************************/
LPSCI_TransferSendDMACallback(dma_handle_t * handle,void * param)98 static void LPSCI_TransferSendDMACallback(dma_handle_t *handle, void *param)
99 {
100     assert(handle);
101     assert(param);
102 
103     lpsci_dma_private_handle_t *lpsciPrivateHandle = (lpsci_dma_private_handle_t *)param;
104 
105     /* Disable LPSCI TX DMA. */
106     LPSCI_EnableTxDMA(lpsciPrivateHandle->base, false);
107 
108     /* Disable interrupt. */
109     DMA_DisableInterrupts(handle->base, handle->channel);
110 
111     lpsciPrivateHandle->handle->txState = kLPSCI_TxIdle;
112 
113     if (lpsciPrivateHandle->handle->callback)
114     {
115         lpsciPrivateHandle->handle->callback(lpsciPrivateHandle->base, lpsciPrivateHandle->handle, kStatus_LPSCI_TxIdle,
116                                              lpsciPrivateHandle->handle->userData);
117     }
118 }
119 
LPSCI_TransferReceiveDMACallback(dma_handle_t * handle,void * param)120 static void LPSCI_TransferReceiveDMACallback(dma_handle_t *handle, void *param)
121 {
122     assert(handle);
123     assert(param);
124 
125     lpsci_dma_private_handle_t *lpsciPrivateHandle = (lpsci_dma_private_handle_t *)param;
126 
127     /* Disable LPSCI RX DMA. */
128     LPSCI_EnableRxDMA(lpsciPrivateHandle->base, false);
129 
130     /* Disable interrupt. */
131     DMA_DisableInterrupts(handle->base, handle->channel);
132 
133     lpsciPrivateHandle->handle->rxState = kLPSCI_RxIdle;
134 
135     if (lpsciPrivateHandle->handle->callback)
136     {
137         lpsciPrivateHandle->handle->callback(lpsciPrivateHandle->base, lpsciPrivateHandle->handle, kStatus_LPSCI_RxIdle,
138                                              lpsciPrivateHandle->handle->userData);
139     }
140 }
141 
LPSCI_TransferCreateHandleDMA(UART0_Type * base,lpsci_dma_handle_t * handle,lpsci_dma_transfer_callback_t callback,void * userData,dma_handle_t * txDmaHandle,dma_handle_t * rxDmaHandle)142 void LPSCI_TransferCreateHandleDMA(UART0_Type *base,
143                                    lpsci_dma_handle_t *handle,
144                                    lpsci_dma_transfer_callback_t callback,
145                                    void *userData,
146                                    dma_handle_t *txDmaHandle,
147                                    dma_handle_t *rxDmaHandle)
148 {
149     assert(handle);
150 
151     uint32_t instance = LPSCI_GetInstance(base);
152 
153     memset(handle, 0, sizeof(lpsci_dma_handle_t));
154 
155     s_dmaPrivateHandle[instance].base = base;
156     s_dmaPrivateHandle[instance].handle = handle;
157 
158     handle->rxState = kLPSCI_RxIdle;
159     handle->txState = kLPSCI_TxIdle;
160 
161     handle->callback = callback;
162     handle->userData = userData;
163 
164 #if defined(FSL_FEATURE_LPSCI_HAS_FIFO) && FSL_FEATURE_LPSCI_HAS_FIFO
165     /* Note:
166        Take care of the RX FIFO, DMA 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        DMA transfer because the water mark is 2.
172      */
173     if (rxDmaHandle)
174     {
175         base->RWFIFO = 1U;
176     }
177 #endif
178 
179     handle->rxDmaHandle = rxDmaHandle;
180     handle->txDmaHandle = txDmaHandle;
181 
182     /* Configure TX. */
183     if (txDmaHandle)
184     {
185         DMA_SetCallback(txDmaHandle, LPSCI_TransferSendDMACallback, &s_dmaPrivateHandle[instance]);
186     }
187 
188     /* Configure RX. */
189     if (rxDmaHandle)
190     {
191         DMA_SetCallback(rxDmaHandle, LPSCI_TransferReceiveDMACallback, &s_dmaPrivateHandle[instance]);
192     }
193 }
194 
LPSCI_TransferSendDMA(UART0_Type * base,lpsci_dma_handle_t * handle,lpsci_transfer_t * xfer)195 status_t LPSCI_TransferSendDMA(UART0_Type *base, lpsci_dma_handle_t *handle, lpsci_transfer_t *xfer)
196 {
197     assert(handle);
198     assert(handle->txDmaHandle);
199     assert(xfer);
200     assert(xfer->data);
201     assert(xfer->dataSize);
202 
203     dma_transfer_config_t xferConfig;
204     status_t status;
205 
206     /* If previous TX not finished. */
207     if (kLPSCI_TxBusy == handle->txState)
208     {
209         status = kStatus_LPSCI_TxBusy;
210     }
211     else
212     {
213         handle->txState = kLPSCI_TxBusy;
214         handle->txDataSizeAll = xfer->dataSize;
215 
216         /* Prepare transfer. */
217         DMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint8_t), (void *)LPSCI_GetDataRegisterAddress(base),
218                             sizeof(uint8_t), xfer->dataSize, kDMA_MemoryToPeripheral);
219 
220         /* Submit transfer. */
221         DMA_SubmitTransfer(handle->txDmaHandle, &xferConfig, kDMA_EnableInterrupt);
222         DMA_StartTransfer(handle->txDmaHandle);
223 
224         /* Enable LPSCI TX DMA. */
225         LPSCI_EnableTxDMA(base, true);
226 
227         status = kStatus_Success;
228     }
229 
230     return status;
231 }
232 
LPSCI_TransferReceiveDMA(UART0_Type * base,lpsci_dma_handle_t * handle,lpsci_transfer_t * xfer)233 status_t LPSCI_TransferReceiveDMA(UART0_Type *base, lpsci_dma_handle_t *handle, lpsci_transfer_t *xfer)
234 {
235     assert(handle);
236     assert(handle->rxDmaHandle);
237     assert(xfer);
238     assert(xfer->data);
239     assert(xfer->dataSize);
240 
241     dma_transfer_config_t xferConfig;
242     status_t status;
243 
244     /* If previous RX not finished. */
245     if (kLPSCI_RxBusy == handle->rxState)
246     {
247         status = kStatus_LPSCI_RxBusy;
248     }
249     else
250     {
251         handle->rxState = kLPSCI_RxBusy;
252         handle->rxDataSizeAll = xfer->dataSize;
253 
254         /* Prepare transfer. */
255         DMA_PrepareTransfer(&xferConfig, (void *)LPSCI_GetDataRegisterAddress(base), sizeof(uint8_t), xfer->data,
256                             sizeof(uint8_t), xfer->dataSize, kDMA_PeripheralToMemory);
257 
258         /* Submit transfer. */
259         DMA_SubmitTransfer(handle->rxDmaHandle, &xferConfig, kDMA_EnableInterrupt);
260         DMA_StartTransfer(handle->rxDmaHandle);
261 
262         /* Enable LPSCI RX DMA. */
263         LPSCI_EnableRxDMA(base, true);
264 
265         status = kStatus_Success;
266     }
267 
268     return status;
269 }
270 
LPSCI_TransferAbortSendDMA(UART0_Type * base,lpsci_dma_handle_t * handle)271 void LPSCI_TransferAbortSendDMA(UART0_Type *base, lpsci_dma_handle_t *handle)
272 {
273     assert(handle);
274     assert(handle->txDmaHandle);
275 
276     /* Disable LPSCI TX DMA. */
277     LPSCI_EnableTxDMA(base, false);
278 
279     /* Stop transfer. */
280     DMA_AbortTransfer(handle->txDmaHandle);
281 
282     /* Write DMA->DSR[DONE] to abort transfer and clear status. */
283     DMA_ClearChannelStatusFlags(handle->txDmaHandle->base, handle->txDmaHandle->channel, kDMA_TransactionsDoneFlag);
284 
285     handle->txState = kLPSCI_TxIdle;
286 }
287 
LPSCI_TransferAbortReceiveDMA(UART0_Type * base,lpsci_dma_handle_t * handle)288 void LPSCI_TransferAbortReceiveDMA(UART0_Type *base, lpsci_dma_handle_t *handle)
289 {
290     assert(handle);
291     assert(handle->rxDmaHandle);
292 
293     /* Disable LPSCI RX DMA. */
294     LPSCI_EnableRxDMA(base, false);
295 
296     /* Stop transfer. */
297     DMA_AbortTransfer(handle->rxDmaHandle);
298 
299     /* Write DMA->DSR[DONE] to abort transfer and clear status. */
300     DMA_ClearChannelStatusFlags(handle->rxDmaHandle->base, handle->rxDmaHandle->channel, kDMA_TransactionsDoneFlag);
301 
302     handle->rxState = kLPSCI_RxIdle;
303 }
304 
LPSCI_TransferGetSendCountDMA(UART0_Type * base,lpsci_dma_handle_t * handle,uint32_t * count)305 status_t LPSCI_TransferGetSendCountDMA(UART0_Type *base, lpsci_dma_handle_t *handle, uint32_t *count)
306 {
307     assert(handle);
308     assert(handle->txDmaHandle);
309     assert(count);
310 
311     if (kLPSCI_TxIdle == handle->txState)
312     {
313         return kStatus_NoTransferInProgress;
314     }
315 
316     *count = handle->txDataSizeAll - DMA_GetRemainingBytes(handle->txDmaHandle->base, handle->txDmaHandle->channel);
317 
318     return kStatus_Success;
319 }
320 
LPSCI_TransferGetReceiveCountDMA(UART0_Type * base,lpsci_dma_handle_t * handle,uint32_t * count)321 status_t LPSCI_TransferGetReceiveCountDMA(UART0_Type *base, lpsci_dma_handle_t *handle, uint32_t *count)
322 {
323     assert(handle);
324     assert(handle->rxDmaHandle);
325     assert(count);
326 
327     if (kLPSCI_RxIdle == handle->rxState)
328     {
329         return kStatus_NoTransferInProgress;
330     }
331 
332     *count = handle->rxDataSizeAll - DMA_GetRemainingBytes(handle->rxDmaHandle->base, handle->rxDmaHandle->channel);
333 
334     return kStatus_Success;
335 }
336