1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2020 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_qspi_edma.h"
10 
11 /*******************************************************************************
12  * Definitions
13  ******************************************************************************/
14 
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.qspi_edma"
18 #endif
19 
20 /*<! Structure definition for qspi_edma_private_handle_t. The structure is private. */
21 typedef struct _qspi_edma_private_handle
22 {
23     QuadSPI_Type *base;
24     qspi_edma_handle_t *handle;
25 } qspi_edma_private_handle_t;
26 
27 /* QSPI EDMA transfer handle. */
28 enum _qspi_edma_tansfer_states
29 {
30     kQSPI_Idle,   /* TX idle. */
31     kQSPI_BusBusy /* RX busy. */
32 };
33 
34 /*!
35  * @brief Used for conversion between `void*` and `uint32_t`.
36  */
37 typedef union pvoid_to_u32
38 {
39     void *pvoid;
40     uint32_t u32;
41 } pvoid_to_u32_t;
42 
43 /*******************************************************************************
44  * Variables
45  ******************************************************************************/
46 
47 /*<! Private handle only used for internally. */
48 static qspi_edma_private_handle_t s_edmaPrivateHandle[FSL_FEATURE_SOC_QuadSPI_COUNT][2];
49 
50 /*******************************************************************************
51  * Prototypes
52  ******************************************************************************/
53 
54 /*!
55  * @brief QSPI EDMA send finished callback function.
56  *
57  * This function is called when QSPI EDMA send finished. It disables the QSPI
58  * TX EDMA request and sends @ref kStatus_QSPI_TxIdle to QSPI callback.
59  *
60  * @param handle The EDMA handle.
61  * @param param Callback function parameter.
62  */
63 static void QSPI_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
64 
65 /*!
66  * @brief QSPI EDMA receive finished callback function.
67  *
68  * This function is called when QSPI EDMA receive finished. It disables the QSPI
69  * RX EDMA request and sends @ref kStatus_QSPI_RxIdle to QSPI callback.
70  *
71  * @param handle The EDMA handle.
72  * @param param Callback function parameter.
73  */
74 static void QSPI_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
75 
76 /*******************************************************************************
77  * Code
78  ******************************************************************************/
79 
QSPI_SendEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)80 static void QSPI_SendEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
81 {
82     qspi_edma_private_handle_t *qspiPrivateHandle = (qspi_edma_private_handle_t *)param;
83 
84     /* Avoid the warning for unused variables. */
85     handle = handle;
86     tcds   = tcds;
87 
88     if (transferDone)
89     {
90         QSPI_TransferAbortSendEDMA(qspiPrivateHandle->base, qspiPrivateHandle->handle);
91 
92         if (NULL != qspiPrivateHandle->handle->callback)
93         {
94             qspiPrivateHandle->handle->callback(qspiPrivateHandle->base, qspiPrivateHandle->handle, kStatus_QSPI_Idle,
95                                                 qspiPrivateHandle->handle->userData);
96         }
97     }
98 }
99 
QSPI_ReceiveEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)100 static void QSPI_ReceiveEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
101 {
102     qspi_edma_private_handle_t *qspiPrivateHandle = (qspi_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         QSPI_TransferAbortReceiveEDMA(qspiPrivateHandle->base, qspiPrivateHandle->handle);
112 
113         if (NULL != qspiPrivateHandle->handle->callback)
114         {
115             qspiPrivateHandle->handle->callback(qspiPrivateHandle->base, qspiPrivateHandle->handle, kStatus_QSPI_Idle,
116                                                 qspiPrivateHandle->handle->userData);
117         }
118     }
119 }
120 
121 /*!
122  * brief Initializes the QSPI handle for send which is used in transactional functions and set the callback.
123  *
124  * param base QSPI peripheral base address
125  * param handle Pointer to qspi_edma_handle_t structure
126  * param callback QSPI callback, NULL means no callback.
127  * param userData User callback function data.
128  * param rxDmaHandle User requested eDMA handle for eDMA transfer
129  */
QSPI_TransferTxCreateHandleEDMA(QuadSPI_Type * base,qspi_edma_handle_t * handle,qspi_edma_callback_t callback,void * userData,edma_handle_t * dmaHandle)130 void QSPI_TransferTxCreateHandleEDMA(QuadSPI_Type *base,
131                                      qspi_edma_handle_t *handle,
132                                      qspi_edma_callback_t callback,
133                                      void *userData,
134                                      edma_handle_t *dmaHandle)
135 {
136     assert(handle);
137 
138     uint32_t instance = QSPI_GetInstance(base);
139 
140     s_edmaPrivateHandle[instance][0].base   = base;
141     s_edmaPrivateHandle[instance][0].handle = handle;
142 
143     (void)memset(handle, 0, sizeof(*handle));
144 
145     handle->state     = (uint32_t)kQSPI_Idle;
146     handle->dmaHandle = dmaHandle;
147 
148     handle->callback = callback;
149     handle->userData = userData;
150 
151     /* Get the watermark value */
152     handle->count = (uint8_t)base->TBCT + 1U;
153 
154     /* Configure TX edma callback */
155     EDMA_SetCallback(handle->dmaHandle, QSPI_SendEDMACallback, &s_edmaPrivateHandle[instance][0]);
156 }
157 
158 /*!
159  * brief Initializes the QSPI handle for receive which is used in transactional functions and set the callback.
160  *
161  * param base QSPI peripheral base address
162  * param handle Pointer to qspi_edma_handle_t structure
163  * param callback QSPI callback, NULL means no callback.
164  * param userData User callback function data.
165  * param rxDmaHandle User requested eDMA handle for eDMA transfer
166  */
QSPI_TransferRxCreateHandleEDMA(QuadSPI_Type * base,qspi_edma_handle_t * handle,qspi_edma_callback_t callback,void * userData,edma_handle_t * dmaHandle)167 void QSPI_TransferRxCreateHandleEDMA(QuadSPI_Type *base,
168                                      qspi_edma_handle_t *handle,
169                                      qspi_edma_callback_t callback,
170                                      void *userData,
171                                      edma_handle_t *dmaHandle)
172 {
173     assert(handle);
174 
175     uint32_t instance = QSPI_GetInstance(base);
176 
177     s_edmaPrivateHandle[instance][1].base   = base;
178     s_edmaPrivateHandle[instance][1].handle = handle;
179 
180     (void)memset(handle, 0, sizeof(*handle));
181 
182     handle->state     = (uint32_t)kQSPI_Idle;
183     handle->dmaHandle = dmaHandle;
184 
185     handle->callback = callback;
186     handle->userData = userData;
187 
188     /* Get the watermark value */
189     handle->count = ((uint8_t)base->RBCT & QuadSPI_RBCT_WMRK_MASK) + 1U;
190 
191     /* Configure RX edma callback */
192     EDMA_SetCallback(handle->dmaHandle, QSPI_ReceiveEDMACallback, &s_edmaPrivateHandle[instance][1]);
193 }
194 
195 /*!
196  * brief Transfers QSPI data using an eDMA non-blocking method.
197  *
198  * This function writes data to the QSPI transmit FIFO. This function is non-blocking.
199  * param base Pointer to QuadSPI Type.
200  * param handle Pointer to qspi_edma_handle_t structure
201  * param xfer QSPI transfer structure.
202  */
QSPI_TransferSendEDMA(QuadSPI_Type * base,qspi_edma_handle_t * handle,qspi_transfer_t * xfer)203 status_t QSPI_TransferSendEDMA(QuadSPI_Type *base, qspi_edma_handle_t *handle, qspi_transfer_t *xfer)
204 {
205     assert(handle && (handle->dmaHandle));
206 
207     edma_transfer_config_t xferConfig;
208     status_t status;
209     pvoid_to_u32_t destAddr;
210 
211     /* If previous TX not finished. */
212     if ((uint8_t)kQSPI_BusBusy == handle->state)
213     {
214         status = kStatus_QSPI_Busy;
215     }
216     else
217     {
218         handle->state = (uint32_t)kQSPI_BusBusy;
219 
220         destAddr.u32 = QSPI_GetTxDataRegisterAddress(base);
221         /* Prepare transfer. */
222         EDMA_PrepareTransfer(&xferConfig, xfer->data, sizeof(uint32_t), destAddr.pvoid, sizeof(uint32_t),
223                              (sizeof(uint32_t) * (uint32_t)handle->count), xfer->dataSize, kEDMA_MemoryToPeripheral);
224 
225         /* Store the initially configured eDMA minor byte transfer count into the QSPI handle */
226         handle->nbytes = (sizeof(uint32_t) * handle->count);
227 
228         /* Submit transfer. */
229         do
230         {
231             status = EDMA_SubmitTransfer(handle->dmaHandle, &xferConfig);
232         } while (status != kStatus_Success);
233 
234         EDMA_StartTransfer(handle->dmaHandle);
235 
236         /* Enable QSPI TX EDMA. */
237         QSPI_EnableDMA(base, (uint32_t)kQSPI_TxBufferFillDMAEnable, true);
238 
239         status = kStatus_Success;
240     }
241 
242     return status;
243 }
244 
245 /*!
246  * brief Receives data using an eDMA non-blocking method.
247  *
248  * This function receive data from the QSPI receive buffer/FIFO. This function is non-blocking. Users shall notice that
249  * this receive size shall not bigger than 64 bytes. As this interface is used to read flash status registers.
250  * For flash contents read, please use AHB bus read, this is much more efficiency.
251  *
252  * param base Pointer to QuadSPI Type.
253  * param handle Pointer to qspi_edma_handle_t structure
254  * param xfer QSPI transfer structure.
255  */
QSPI_TransferReceiveEDMA(QuadSPI_Type * base,qspi_edma_handle_t * handle,qspi_transfer_t * xfer)256 status_t QSPI_TransferReceiveEDMA(QuadSPI_Type *base, qspi_edma_handle_t *handle, qspi_transfer_t *xfer)
257 {
258     assert(handle && (handle->dmaHandle));
259 
260     edma_transfer_config_t xferConfig;
261     status_t status;
262     pvoid_to_u32_t srcAddr;
263 
264     /* If previous TX not finished. */
265     if ((uint32_t)kQSPI_BusBusy == handle->state)
266     {
267         status = kStatus_QSPI_Busy;
268     }
269     else
270     {
271         handle->state = (uint32_t)kQSPI_BusBusy;
272 
273         srcAddr.u32 = QSPI_GetRxDataRegisterAddress(base);
274         /* Prepare transfer. */
275         EDMA_PrepareTransfer(&xferConfig, srcAddr.pvoid, sizeof(uint32_t), xfer->data, sizeof(uint32_t),
276                              (sizeof(uint32_t) * (uint32_t)handle->count), xfer->dataSize, kEDMA_MemoryToMemory);
277 
278         /* Store the initially configured eDMA minor byte transfer count into the QSPI handle */
279         handle->nbytes = (sizeof(uint32_t) * handle->count);
280         /* Submit transfer. */
281         do
282         {
283             status = EDMA_SubmitTransfer(handle->dmaHandle, &xferConfig);
284         } while (status != kStatus_Success);
285 
286         handle->dmaHandle->base->TCD[handle->dmaHandle->channel].ATTR |= DMA_ATTR_SMOD(0x5U);
287         EDMA_StartTransfer(handle->dmaHandle);
288 
289         /* Enable QSPI TX EDMA. */
290         QSPI_EnableDMA(base, (uint32_t)kQSPI_RxBufferDrainDMAEnable, true);
291 
292         status = kStatus_Success;
293     }
294 
295     return status;
296 }
297 
298 /*!
299  * brief Aborts the sent data using eDMA.
300  *
301  * This function aborts the sent data using eDMA.
302  *
303  * param base QSPI peripheral base address.
304  * param handle Pointer to qspi_edma_handle_t structure
305  */
QSPI_TransferAbortSendEDMA(QuadSPI_Type * base,qspi_edma_handle_t * handle)306 void QSPI_TransferAbortSendEDMA(QuadSPI_Type *base, qspi_edma_handle_t *handle)
307 {
308     assert(handle && (handle->dmaHandle));
309 
310     /* Disable QSPI TX EDMA. */
311     QSPI_EnableDMA(base, (uint32_t)kQSPI_TxBufferFillDMAEnable, false);
312 
313     /* Stop transfer. */
314     EDMA_AbortTransfer(handle->dmaHandle);
315 
316     handle->state = (uint32_t)kQSPI_Idle;
317 }
318 
319 /*!
320  * brief Aborts the receive data using eDMA.
321  *
322  * This function abort receive data which using eDMA.
323  *
324  * param base QSPI peripheral base address.
325  * param handle Pointer to qspi_edma_handle_t structure
326  */
QSPI_TransferAbortReceiveEDMA(QuadSPI_Type * base,qspi_edma_handle_t * handle)327 void QSPI_TransferAbortReceiveEDMA(QuadSPI_Type *base, qspi_edma_handle_t *handle)
328 {
329     assert(handle && (handle->dmaHandle));
330 
331     /* Disable QSPI RX EDMA. */
332     QSPI_EnableDMA(base, (uint32_t)kQSPI_RxBufferDrainDMAEnable, false);
333 
334     /* Stop transfer. */
335     EDMA_AbortTransfer(handle->dmaHandle);
336 
337     handle->state = (uint32_t)kQSPI_Idle;
338 }
339 
340 /*!
341  * brief Gets the transferred counts of send.
342  *
343  * param base Pointer to QuadSPI Type.
344  * param handle Pointer to qspi_edma_handle_t structure.
345  * param count Bytes sent.
346  * retval kStatus_Success Succeed get the transfer count.
347  * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress.
348  */
QSPI_TransferGetSendCountEDMA(QuadSPI_Type * base,qspi_edma_handle_t * handle,size_t * count)349 status_t QSPI_TransferGetSendCountEDMA(QuadSPI_Type *base, qspi_edma_handle_t *handle, size_t *count)
350 {
351     assert(handle);
352 
353     status_t status = kStatus_Success;
354 
355     if (handle->state != (uint32_t)kQSPI_BusBusy)
356     {
357         status = kStatus_NoTransferInProgress;
358     }
359     else
360     {
361         *count = (handle->transferSize -
362                   (uint32_t)handle->nbytes *
363                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
364     }
365 
366     return status;
367 }
368 
369 /*!
370  * brief Gets the status of the receive transfer.
371  *
372  * param base Pointer to QuadSPI Type.
373  * param handle Pointer to qspi_edma_handle_t structure
374  * param count Bytes received.
375  * retval kStatus_Success Succeed get the transfer count.
376  * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress.
377  */
QSPI_TransferGetReceiveCountEDMA(QuadSPI_Type * base,qspi_edma_handle_t * handle,size_t * count)378 status_t QSPI_TransferGetReceiveCountEDMA(QuadSPI_Type *base, qspi_edma_handle_t *handle, size_t *count)
379 {
380     assert(handle);
381 
382     status_t status = kStatus_Success;
383 
384     if (handle->state != (uint32_t)kQSPI_BusBusy)
385     {
386         status = kStatus_NoTransferInProgress;
387     }
388     else
389     {
390         *count = (handle->transferSize -
391                   (uint32_t)handle->nbytes *
392                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
393     }
394 
395     return status;
396 }
397