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