1 /*
2  * Copyright (c) 2016, 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_flexspi_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.flexspi_edma"
18 #endif
19 
20 /*<! Structure definition for flexspi_edma_private_handle_t. The structure is private. */
21 typedef struct _flexspi_edma_private_handle
22 {
23     FLEXSPI_Type *base;
24     flexspi_edma_handle_t *handle;
25 } flexspi_edma_private_handle_t;
26 
27 /* FLEXSPI EDMA transfer handle, _flexspi_edma_tansfer_states. */
28 enum
29 {
30     kFLEXSPI_Idle, /* FLEXSPI Bus idle. */
31     kFLEXSPI_Busy  /* FLEXSPI Bus busy. */
32 };
33 
34 /*******************************************************************************
35  * Variables
36  ******************************************************************************/
37 
38 /*! @brief Pointers to flexspi bases for each instance. */
39 static FLEXSPI_Type *const s_flexspiBases[] = FLEXSPI_BASE_PTRS;
40 
41 /*<! Private handle only used for internally. */
42 static flexspi_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_flexspiBases)];
43 
44 /*******************************************************************************
45  * Prototypes
46  ******************************************************************************/
47 
48 /*!
49  * @brief FLEXSPI EDMA transfer finished callback function.
50  *
51  * This function is called when FLEXSPI EDMA transfer finished. It disables the FLEXSPI
52  * TX/RX EDMA request and sends status to FLEXSPI callback.
53  *
54  * @param handle The EDMA handle.
55  * @param param Callback function parameter.
56  */
57 static void FLEXSPI_TransferEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
58 
59 /*******************************************************************************
60  * Code
61  ******************************************************************************/
FLEXSPI_CalculatePower(uint8_t value)62 static uint8_t FLEXSPI_CalculatePower(uint8_t value)
63 {
64     uint8_t power = 0;
65     while (value >> 1 != 0U)
66     {
67         power++;
68         value = value >> 1;
69     }
70 
71     return power;
72 }
FLEXSPI_TransferEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)73 static void FLEXSPI_TransferEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
74 {
75     flexspi_edma_private_handle_t *flexspiPrivateHandle = (flexspi_edma_private_handle_t *)param;
76 
77     /* Avoid warning for unused parameters. */
78     handle = handle;
79     tcds   = tcds;
80 
81     if (transferDone)
82     {
83         /* Wait for bus idle. */
84         while (!FLEXSPI_GetBusIdleStatus(flexspiPrivateHandle->base))
85         {
86         }
87         /* Disable transfer. */
88         FLEXSPI_TransferAbortEDMA(flexspiPrivateHandle->base, flexspiPrivateHandle->handle);
89 
90         if (flexspiPrivateHandle->handle->completionCallback != NULL)
91         {
92             flexspiPrivateHandle->handle->completionCallback(flexspiPrivateHandle->base, flexspiPrivateHandle->handle,
93                                                              kStatus_Success, flexspiPrivateHandle->handle->userData);
94         }
95     }
96 }
97 
98 /*!
99  * brief Initializes the FLEXSPI handle for transfer which is used in transactional functions and set the callback.
100  *
101  * param base FLEXSPI peripheral base address
102  * param handle Pointer to flexspi_edma_handle_t structure
103  * param callback FLEXSPI callback, NULL means no callback.
104  * param userData User callback function data.
105  * param txDmaHandle User requested DMA handle for TX DMA transfer.
106  * param rxDmaHandle User requested DMA handle for RX DMA transfer.
107  */
FLEXSPI_TransferCreateHandleEDMA(FLEXSPI_Type * base,flexspi_edma_handle_t * handle,flexspi_edma_callback_t callback,void * userData,edma_handle_t * txDmaHandle,edma_handle_t * rxDmaHandle)108 void FLEXSPI_TransferCreateHandleEDMA(FLEXSPI_Type *base,
109                                       flexspi_edma_handle_t *handle,
110                                       flexspi_edma_callback_t callback,
111                                       void *userData,
112                                       edma_handle_t *txDmaHandle,
113                                       edma_handle_t *rxDmaHandle)
114 {
115     assert(handle);
116 
117     uint32_t instance = FLEXSPI_GetInstance(base);
118 
119     s_edmaPrivateHandle[instance].base   = base;
120     s_edmaPrivateHandle[instance].handle = handle;
121 
122     (void)memset(handle, 0, sizeof(*handle));
123 
124     handle->state       = kFLEXSPI_Idle;
125     handle->txDmaHandle = txDmaHandle;
126     handle->rxDmaHandle = rxDmaHandle;
127     handle->nsize       = kFLEXPSI_EDMAnSize1Bytes;
128 
129     handle->completionCallback = callback;
130     handle->userData           = userData;
131 }
132 
133 /*!
134  * brief Update FLEXSPI EDMA transfer source data transfer size(SSIZE) and destination data transfer size(DSIZE).
135  *
136  * param base FLEXSPI peripheral base address
137  * param handle Pointer to flexspi_edma_handle_t structure
138  * param nsize FLEXSPI DMA transfer data transfer size(SSIZE/DSIZE), by default the size is
139  * kFLEXPSI_EDMAnSize1Bytes(one byte).
140  * see flexspi_edma_transfer_nsize_t               .
141  */
FLEXSPI_TransferUpdateSizeEDMA(FLEXSPI_Type * base,flexspi_edma_handle_t * handle,flexspi_edma_transfer_nsize_t nsize)142 void FLEXSPI_TransferUpdateSizeEDMA(FLEXSPI_Type *base,
143                                     flexspi_edma_handle_t *handle,
144                                     flexspi_edma_transfer_nsize_t nsize)
145 {
146     handle->nsize = nsize;
147 }
148 
149 /*!
150  * brief Transfers FLEXSPI data using an eDMA non-blocking method.
151  *
152  * This function writes/receives data to/from the FLEXSPI transmit/receive FIFO. This function is non-blocking.
153  * param base FLEXSPI peripheral base address.
154  * param handle Pointer to flexspi_edma_handle_t structure
155  * param xfer FLEXSPI transfer structure.
156  * retval kStatus_FLEXSPI_Busy     FLEXSPI is busy transfer.
157  * retval kStatus_InvalidArgument  The watermark configuration is invalid, the watermark should be power of
158                                     2 to do successfully EDMA transfer.
159  * retval kStatus_Success          FLEXSPI successfully start edma transfer.
160  */
FLEXSPI_TransferEDMA(FLEXSPI_Type * base,flexspi_edma_handle_t * handle,flexspi_transfer_t * xfer)161 status_t FLEXSPI_TransferEDMA(FLEXSPI_Type *base, flexspi_edma_handle_t *handle, flexspi_transfer_t *xfer)
162 {
163     uint32_t configValue = 0;
164     status_t result      = kStatus_Success;
165     edma_transfer_config_t xferConfig;
166     uint32_t instance = FLEXSPI_GetInstance(base);
167     uint8_t power     = 0;
168 
169     assert(handle);
170     assert(xfer);
171 
172     /* Check if the FLEXSPI bus is idle - if not return busy status. */
173     if (handle->state != (uint32_t)kFLEXSPI_Idle)
174     {
175         result = kStatus_FLEXSPI_Busy;
176     }
177     else
178     {
179         handle->transferSize = xfer->dataSize;
180         handle->state        = kFLEXSPI_Busy;
181 
182         /* Clear sequence pointer before sending data to external devices. */
183         base->FLSHCR2[xfer->port] |= FLEXSPI_FLSHCR2_CLRINSTRPTR_MASK;
184 
185         /* Clear former pending status before start this transfer. */
186         base->INTR |= FLEXSPI_INTR_AHBCMDERR_MASK | FLEXSPI_INTR_IPCMDERR_MASK | FLEXSPI_INTR_AHBCMDGE_MASK |
187                       FLEXSPI_INTR_IPCMDGE_MASK;
188 
189         /* Configure base address. */
190         base->IPCR0 = xfer->deviceAddress;
191 
192         /* Reset fifos. */
193         base->IPTXFCR |= FLEXSPI_IPTXFCR_CLRIPTXF_MASK;
194         base->IPRXFCR |= FLEXSPI_IPRXFCR_CLRIPRXF_MASK;
195 
196         /* Configure data size. */
197         if ((xfer->cmdType == kFLEXSPI_Read) || (xfer->cmdType == kFLEXSPI_Write))
198         {
199             configValue = FLEXSPI_IPCR1_IDATSZ(xfer->dataSize);
200         }
201 
202         /* Configure sequence ID. */
203         configValue |= FLEXSPI_IPCR1_ISEQID(xfer->seqIndex) | FLEXSPI_IPCR1_ISEQNUM((uint32_t)xfer->SeqNumber - 1U);
204         base->IPCR1 = configValue;
205     }
206 
207     if ((xfer->cmdType == kFLEXSPI_Write) || (xfer->cmdType == kFLEXSPI_Config))
208     {
209         handle->count = (uint8_t)((base->IPTXFCR & FLEXSPI_IPTXFCR_TXWMRK_MASK) >> FLEXSPI_IPTXFCR_TXWMRK_SHIFT) + 1U;
210 
211         if (xfer->dataSize < 8U * (uint32_t)handle->count)
212         {
213             handle->nbytes = (uint8_t)xfer->dataSize;
214         }
215         else
216         {
217             /* Check the handle->count is power of 2 */
218             if (((handle->count) & (handle->count - 1U)) != 0U)
219             {
220                 return kStatus_InvalidArgument;
221             }
222             /* Store the initially configured eDMA minor byte transfer count into the FLEXSPI handle */
223             handle->nbytes = (8U * handle->count);
224         }
225 
226         power = FLEXSPI_CalculatePower(8U * handle->count);
227 
228         /* Prepare transfer. */
229         EDMA_PrepareTransfer(&xferConfig, xfer->data, (uint32_t)handle->nsize,
230                              (void *)(uint32_t *)FLEXSPI_GetTxFifoAddress(base), (uint32_t)handle->nsize,
231                              (uint32_t)handle->nbytes, xfer->dataSize, kEDMA_MemoryToMemory);
232 
233         /* Submit transfer. */
234         (void)EDMA_SubmitTransfer(handle->txDmaHandle, &xferConfig);
235         handle->txDmaHandle->base->TCD[handle->txDmaHandle->channel].ATTR |= DMA_ATTR_DMOD(power);
236         EDMA_SetCallback(handle->txDmaHandle, FLEXSPI_TransferEDMACallback,
237                          &s_edmaPrivateHandle[FLEXSPI_GetInstance(base)]);
238         EDMA_StartTransfer(handle->txDmaHandle);
239 
240         /* Enable FLEXSPI TX EDMA. */
241         FLEXSPI_EnableTxDMA(base, true);
242 
243         /* Start Transfer. */
244         base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
245     }
246     else if (xfer->cmdType == kFLEXSPI_Read)
247     {
248         handle->count = (uint8_t)((base->IPRXFCR & FLEXSPI_IPRXFCR_RXWMRK_MASK) >> FLEXSPI_IPRXFCR_RXWMRK_SHIFT) + 1U;
249 
250         if (xfer->dataSize < 8U * (uint32_t)handle->count)
251         {
252             handle->nbytes = (uint8_t)xfer->dataSize;
253         }
254         else
255         {
256             /* Check the handle->count is power of 2 */
257             if (((handle->count) & (handle->count - 1U)) != 0U)
258             {
259                 return kStatus_InvalidArgument;
260             }
261             /* Store the initially configured eDMA minor byte transfer count into the FLEXSPI handle */
262             handle->nbytes = (8U * handle->count);
263         }
264 
265         power = FLEXSPI_CalculatePower(8U * handle->count);
266 
267         /* Prepare transfer. */
268         EDMA_PrepareTransfer(&xferConfig, (void *)(uint32_t *)FLEXSPI_GetRxFifoAddress(base), (uint32_t)handle->nsize,
269                              xfer->data, (uint32_t)handle->nsize, (uint32_t)handle->nbytes, xfer->dataSize,
270                              kEDMA_MemoryToMemory);
271 
272         /* Submit transfer. */
273         (void)EDMA_SubmitTransfer(handle->rxDmaHandle, &xferConfig);
274         handle->rxDmaHandle->base->TCD[handle->rxDmaHandle->channel].ATTR |= DMA_ATTR_SMOD(power);
275         EDMA_SetCallback(handle->rxDmaHandle, FLEXSPI_TransferEDMACallback, &s_edmaPrivateHandle[instance]);
276         EDMA_StartTransfer(handle->rxDmaHandle);
277 
278         /* Enable FLEXSPI RX EDMA. */
279         FLEXSPI_EnableRxDMA(base, true);
280 
281         /* Start Transfer. */
282         base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
283     }
284     else
285     {
286         /* Start Transfer. */
287         base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
288         /* Wait for bus idle. */
289         while (!FLEXSPI_GetBusIdleStatus(base))
290         {
291         }
292         result = FLEXSPI_CheckAndClearError(base, base->INTR);
293 
294         handle->state = kFLEXSPI_Idle;
295 
296         if (handle->completionCallback != NULL)
297         {
298             handle->completionCallback(base, handle, result, handle->userData);
299         }
300     }
301 
302     return result;
303 }
304 
305 /*!
306  * brief Aborts the transfer data using eDMA.
307  *
308  * This function aborts the transfer data using eDMA.
309  *
310  * param base FLEXSPI peripheral base address.
311  * param handle Pointer to flexspi_edma_handle_t structure
312  */
FLEXSPI_TransferAbortEDMA(FLEXSPI_Type * base,flexspi_edma_handle_t * handle)313 void FLEXSPI_TransferAbortEDMA(FLEXSPI_Type *base, flexspi_edma_handle_t *handle)
314 {
315     assert(handle);
316 
317     if ((base->IPTXFCR & FLEXSPI_IPTXFCR_TXDMAEN_MASK) != 0x00U)
318     {
319         FLEXSPI_EnableTxDMA(base, false);
320         EDMA_AbortTransfer(handle->txDmaHandle);
321     }
322 
323     if ((base->IPRXFCR & FLEXSPI_IPRXFCR_RXDMAEN_MASK) != 0x00U)
324     {
325         FLEXSPI_EnableRxDMA(base, false);
326         EDMA_AbortTransfer(handle->rxDmaHandle);
327     }
328 
329     handle->state = kFLEXSPI_Idle;
330 }
331 
FLEXSPI_TransferGetTransferCountEDMA(FLEXSPI_Type * base,flexspi_edma_handle_t * handle,size_t * count)332 status_t FLEXSPI_TransferGetTransferCountEDMA(FLEXSPI_Type *base, flexspi_edma_handle_t *handle, size_t *count)
333 {
334     assert(handle);
335     assert(count);
336 
337     status_t result = kStatus_Success;
338 
339     if (handle->state != (uint32_t)kFLEXSPI_Busy)
340     {
341         result = kStatus_NoTransferInProgress;
342     }
343     else
344     {
345         if ((base->IPRXFCR & FLEXSPI_IPRXFCR_RXDMAEN_MASK) != 0x00U)
346         {
347             *count = (handle->transferSize -
348                       (uint32_t)handle->nbytes *
349                           EDMA_GetRemainingMajorLoopCount(handle->rxDmaHandle->base, handle->rxDmaHandle->channel));
350         }
351         else if ((base->IPTXFCR & FLEXSPI_IPTXFCR_TXDMAEN_MASK) != 0x00U)
352         {
353             *count = (handle->transferSize -
354                       (uint32_t)handle->nbytes *
355                           EDMA_GetRemainingMajorLoopCount(handle->txDmaHandle->base, handle->txDmaHandle->channel));
356         }
357         else
358         {
359             ; /* Intentional empty for MISRA C-2012 rule 15.7. */
360         }
361     }
362 
363     return result;
364 }
365