1 /*
2  * Copyright 2023-2024 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_xspi_edma.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.xspi_edma"
17 #endif
18 
19 /*<! Structure definition for xspi_edma_private_handle_t. The structure is private. */
20 typedef struct _xspi_edma_private_handle
21 {
22     XSPI_Type *base;
23     xspi_edma_handle_t *handle;
24 } xspi_edma_private_handle_t;
25 
26 /* XSPI EDMA transfer handle, _xspi_edma_tansfer_states. */
27 enum
28 {
29     kXSPI_Idle, /* XSPI Bus idle. */
30     kXSPI_Busy  /* XSPI Bus busy. */
31 };
32 
33 /*******************************************************************************
34  * Variables
35  ******************************************************************************/
36 
37 /*! @brief Pointers to xspi bases for each instance. */
38 static XSPI_Type *const s_xspiBases[] = XSPI_BASE_PTRS;
39 
40 /*<! Private handle only used for internally. */
41 static xspi_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_xspiBases)];
42 
43 /*******************************************************************************
44  * Prototypes
45  ******************************************************************************/
46 
47 /*!
48  * @brief XSPI EDMA transfer finished callback function.
49  *
50  * This function is called when XSPI EDMA transfer finished. It disables the XSPI
51  * TX/RX EDMA request and sends status to XSPI callback.
52  *
53  * @param handle The EDMA handle.
54  * @param param Callback function parameter.
55  */
56 static void XSPI_TransferEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
57 
58 /*******************************************************************************
59  * Code
60  ******************************************************************************/
XSPI_CalculatePower(uint8_t value)61 static uint8_t XSPI_CalculatePower(uint8_t value)
62 {
63     uint8_t power = 0;
64     while (value >> 1 != 0U)
65     {
66         power++;
67         value = value >> 1;
68     }
69 
70     return power;
71 }
XSPI_TransferEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)72 static void XSPI_TransferEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
73 {
74     xspi_edma_private_handle_t *xspiPrivateHandle = (xspi_edma_private_handle_t *)param;
75 
76     /* Avoid warning for unused parameters. */
77     handle = handle;
78     tcds   = tcds;
79 
80     if (transferDone)
81     {
82         /* Wait for bus idle. */
83         while (!XSPI_GetBusIdleStatus(xspiPrivateHandle->base))
84         {
85         }
86         XSPI_EnableTxDMA(xspiPrivateHandle->base, false);
87         XSPI_EnableRxDMA(xspiPrivateHandle->base, false);
88         /* Disable transfer. */
89         XSPI_TransferAbortEDMA(xspiPrivateHandle->base, xspiPrivateHandle->handle);
90         if (xspiPrivateHandle->handle->completionCallback != NULL)
91         {
92             xspiPrivateHandle->handle->completionCallback(xspiPrivateHandle->base, xspiPrivateHandle->handle,
93                                                           kStatus_Success, xspiPrivateHandle->handle->userData);
94         }
95     }
96 }
97 
98 /*!
99  * brief Initializes the XSPI handle for transfer which is used in transactional functions and set the callback.
100  *
101  * param base XSPI peripheral base address
102  * param handle Pointer to xspi_edma_handle_t structure
103  * param callback XSPI 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  */
XSPI_TransferCreateHandleEDMA(XSPI_Type * base,xspi_edma_handle_t * handle,xspi_edma_callback_t callback,void * userData,edma_handle_t * txDmaHandle,edma_handle_t * rxDmaHandle)108 void XSPI_TransferCreateHandleEDMA(XSPI_Type *base,
109                                    xspi_edma_handle_t *handle,
110                                    xspi_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 = XSPI_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       = kXSPI_Idle;
125     handle->txDmaHandle = txDmaHandle;
126     handle->rxDmaHandle = rxDmaHandle;
127     handle->nsize       = kXSPI_EDMAnSize4Bytes;
128 
129     handle->completionCallback = callback;
130     handle->userData           = userData;
131 }
132 
133 /*!
134  * brief Update XSPI EDMA transfer source data transfer size(SSIZE) and destination data transfer size(DSIZE).
135  *
136  * param base XSPI peripheral base address
137  * param handle Pointer to xspi_edma_handle_t structure
138  * param nsize XSPI DMA transfer data transfer size(SSIZE/DSIZE), by default the size is
139  * kXSPI_EDMAnSize1Bytes(one byte).
140  * see xspi_edma_transfer_nsize_t               .
141  */
XSPI_TransferUpdateSizeEDMA(XSPI_Type * base,xspi_edma_handle_t * handle,xspi_edma_transfer_nsize_t nsize)142 void XSPI_TransferUpdateSizeEDMA(XSPI_Type *base, xspi_edma_handle_t *handle, xspi_edma_transfer_nsize_t nsize)
143 {
144     handle->nsize = nsize;
145 }
146 
147 /*!
148  * brief Transfers XSPI data using an eDMA non-blocking method.
149  *
150  * This function writes/receives data to/from the XSPI transmit/receive FIFO. This function is non-blocking.
151  * param base XSPI peripheral base address.
152  * param handle Pointer to xspi_edma_handle_t structure
153  * param xfer XSPI transfer structure.
154  * retval kStatus_XSPI_Busy     XSPI is busy transfer.
155  * retval kStatus_InvalidArgument  The watermark configuration is invalid, the watermark should be power of
156                                     2 to do successfully EDMA transfer.
157  * retval kStatus_Success          XSPI successfully start edma transfer.
158  */
XSPI_TransferEDMA(XSPI_Type * base,xspi_edma_handle_t * handle,xspi_transfer_t * xfer)159 status_t XSPI_TransferEDMA(XSPI_Type *base, xspi_edma_handle_t *handle, xspi_transfer_t *xfer)
160 {
161     //    uint32_t configValue = 0;
162     status_t status = kStatus_Success;
163     edma_transfer_config_t xferConfig;
164     uint32_t instance = XSPI_GetInstance(base);
165     uint8_t power     = 0;
166 
167     assert(handle);
168     assert(xfer);
169 
170     /* Check if the XSPI bus is idle - if not return busy status. */
171     if (handle->state != (uint32_t)kXSPI_Idle)
172     {
173         status = kStatus_XSPI_Busy;
174         return status;
175     }
176     else
177     {
178         handle->transferSize = xfer->dataSize;
179         handle->state        = kXSPI_Busy;
180     }
181 
182     if ((xfer->cmdType == kXSPI_Write) || (xfer->cmdType == kXSPI_Config))
183     {
184         power          = XSPI_CalculatePower(4U * handle->count);
185         handle->nbytes = xfer->dataSize;
186         /* Prepare transfer. */
187         EDMA_PrepareTransfer(&xferConfig, xfer->data, (uint32_t)handle->nsize,
188                              (void *)(uint32_t *)XSPI_GetTxFifoAddress(base), (uint32_t)handle->nsize,
189                              (uint32_t)handle->nbytes, xfer->dataSize, kEDMA_MemoryToPeripheral);
190 
191         /* Submit transfer. */
192         (void)EDMA_SubmitTransfer(handle->txDmaHandle, &xferConfig);
193         EDMA_SetModulo(handle->txDmaHandle->base, handle->txDmaHandle->channel, kEDMA_ModuloDisable,
194                        (edma_modulo_t)power);
195         EDMA_SetCallback(handle->txDmaHandle, XSPI_TransferEDMACallback, &s_edmaPrivateHandle[XSPI_GetInstance(base)]);
196 
197         /* Clear TX buffer pointer. */
198         XSPI_ClearTxBuffer(base);
199         /* Blocking until TX buffer is unlocked. */
200         base->TBCT = 256UL - (xfer->dataSize / 4UL - 1UL);
201         status     = XSPI_StartIpAccess(base, xfer->deviceAddress, xfer->seqIndex, xfer->dataSize, xfer->targetGroup,
202                                         xfer->lockArbitration);
203         if (status != kStatus_Success)
204         {
205             return status;
206         }
207         while (XSPI_CheckTxBuffLockOpen(base) == false)
208         {
209         }
210         /* Enable XSPI TX EDMA. */
211         XSPI_EnableTxDMA(base, true);
212         EDMA_StartTransfer(handle->txDmaHandle);
213 
214         while (XSPI_CheckIPAccessAsserted(base))
215         {
216         }
217     }
218     else if (xfer->cmdType == kXSPI_Read)
219     {
220         status = XSPI_StartIpAccess(base, xfer->deviceAddress, xfer->seqIndex, xfer->dataSize, xfer->targetGroup,
221                                     xfer->lockArbitration);
222         if (status != kStatus_Success)
223         {
224             return status;
225         }
226         XSPI_ClearRxBuffer(base);
227         handle->count = (uint8_t)(base->RBCT) + 1U;
228 
229         if (xfer->dataSize < 4U * (uint32_t)handle->count)
230         {
231             handle->nbytes = (uint8_t)xfer->dataSize;
232         }
233         else
234         {
235             /* Check the handle->count is power of 2 */
236             if (((handle->count) & (handle->count - 1U)) != 0U)
237             {
238                 return kStatus_InvalidArgument;
239             }
240             /* Store the initially configured eDMA minor byte transfer count into the XSPI handle */
241             handle->nbytes = (4UL * handle->count);
242         }
243 
244         power = XSPI_CalculatePower(4U * handle->count);
245 
246         /* Prepare transfer. */
247         EDMA_PrepareTransfer(&xferConfig, (void *)(uint32_t *)XSPI_GetRxFifoAddress(base), (uint32_t)handle->nsize,
248                              xfer->data, (uint32_t)handle->nsize, xfer->dataSize, xfer->dataSize, kEDMA_MemoryToMemory);
249 
250         /* Submit transfer. */
251         (void)EDMA_SubmitTransfer(handle->rxDmaHandle, &xferConfig);
252         EDMA_SetModulo(handle->txDmaHandle->base, handle->txDmaHandle->channel, (edma_modulo_t)power,
253                        kEDMA_ModuloDisable);
254         EDMA_SetCallback(handle->rxDmaHandle, XSPI_TransferEDMACallback, &s_edmaPrivateHandle[instance]);
255         EDMA_StartTransfer(handle->rxDmaHandle);
256 
257         /* Enable XSPI RX EDMA. */
258         XSPI_EnableRxDMA(base, true);
259     }
260     else
261     {
262         status = XSPI_StartIpAccess(base, xfer->deviceAddress, xfer->seqIndex, 0UL, xfer->targetGroup,
263                                     xfer->lockArbitration);
264         if (status != kStatus_Success)
265         {
266             return status;
267         }
268         /* Wait for bus idle. */
269         while (!XSPI_GetBusIdleStatus(base))
270         {
271         }
272         status = XSPI_CheckAndClearError(base, base->ERRSTAT);
273 
274         handle->state = kXSPI_Idle;
275 
276         if (handle->completionCallback != NULL)
277         {
278             handle->completionCallback(base, handle, status, handle->userData);
279         }
280     }
281 
282     return status;
283 }
284 
285 /*!
286  * brief Aborts the transfer data using eDMA.
287  *
288  * This function aborts the transfer data using eDMA.
289  *
290  * param base XSPI peripheral base address.
291  * param handle Pointer to xspi_edma_handle_t structure
292  */
XSPI_TransferAbortEDMA(XSPI_Type * base,xspi_edma_handle_t * handle)293 void XSPI_TransferAbortEDMA(XSPI_Type *base, xspi_edma_handle_t *handle)
294 {
295     assert(handle);
296 
297     if ((base->SR & XSPI_SR_TXWA_MASK) != 0x00U)
298     {
299         XSPI_EnableTxDMA(base, false);
300         EDMA_AbortTransfer(handle->txDmaHandle);
301     }
302 
303     if ((base->SR & XSPI_SR_RXDMA_MASK) != 0x00U)
304     {
305         XSPI_EnableRxDMA(base, false);
306         EDMA_AbortTransfer(handle->rxDmaHandle);
307     }
308 
309     handle->state = kXSPI_Idle;
310 }
311 
XSPI_TransferGetTransferCountEDMA(XSPI_Type * base,xspi_edma_handle_t * handle,size_t * count)312 status_t XSPI_TransferGetTransferCountEDMA(XSPI_Type *base, xspi_edma_handle_t *handle, size_t *count)
313 {
314     assert(handle);
315     assert(count);
316 
317     status_t result = kStatus_Success;
318 
319     if (handle->state != (uint32_t)kXSPI_Busy)
320     {
321         result = kStatus_NoTransferInProgress;
322     }
323     else
324     {
325         if ((base->SR & XSPI_SR_RXDMA_MASK) != 0x00U)
326         {
327             *count = (handle->transferSize -
328                       (uint32_t)handle->nbytes *
329                           EDMA_GetRemainingMajorLoopCount(handle->rxDmaHandle->base, handle->rxDmaHandle->channel));
330         }
331         else if ((base->SR & XSPI_SR_TXWA_MASK) != 0x00U)
332         {
333             *count = (handle->transferSize -
334                       (uint32_t)handle->nbytes *
335                           EDMA_GetRemainingMajorLoopCount(handle->txDmaHandle->base, handle->txDmaHandle->channel));
336         }
337         else
338         {
339             ; /* Intentional empty for MISRA C-2012 rule 15.7. */
340         }
341     }
342 
343     return result;
344 }
345