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