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_flexio_spi_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.flexio_spi_edma"
18 #endif
19 
20 /*<! Structure definition for spi_edma_private_handle_t. The structure is private. */
21 typedef struct _flexio_spi_master_edma_private_handle
22 {
23     FLEXIO_SPI_Type *base;
24     flexio_spi_master_edma_handle_t *handle;
25 } flexio_spi_master_edma_private_handle_t;
26 
27 /*******************************************************************************
28  * Prototypes
29  ******************************************************************************/
30 
31 /*!
32  * @brief EDMA callback function for FLEXIO SPI send transfer.
33  *
34  * @param handle EDMA handle pointer.
35  * @param param Callback function parameter.
36  */
37 static void FLEXIO_SPI_TxEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
38 
39 /*!
40  * @brief EDMA callback function for FLEXIO SPI receive transfer.
41  *
42  * @param handle EDMA handle pointer.
43  * @param param Callback function parameter.
44  */
45 static void FLEXIO_SPI_RxEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
46 
47 /*!
48  * @brief EDMA config for FLEXIO SPI transfer.
49  *
50  * @param base pointer to FLEXIO_SPI_Type structure.
51  * @param handle pointer to flexio_spi_master_edma_handle_t structure to store the transfer state.
52  * @param xfer Pointer to flexio spi transfer structure.
53  */
54 static void FLEXIO_SPI_EDMAConfig(FLEXIO_SPI_Type *base,
55                                   flexio_spi_master_edma_handle_t *handle,
56                                   flexio_spi_transfer_t *xfer);
57 
58 /*******************************************************************************
59  * Variables
60  ******************************************************************************/
61 
62 /* Dummy data used to send */
63 static const uint16_t s_dummyData = FLEXIO_SPI_DUMMYDATA;
64 
65 /*< @brief user configurable flexio spi handle count. */
66 #define FLEXIO_SPI_HANDLE_COUNT 2
67 
68 /*<! Private handle only used for internally. */
69 static flexio_spi_master_edma_private_handle_t s_edmaPrivateHandle[FLEXIO_SPI_HANDLE_COUNT];
70 
71 /*******************************************************************************
72  * Code
73  ******************************************************************************/
74 
FLEXIO_SPI_TxEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)75 static void FLEXIO_SPI_TxEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
76 {
77     tcds                                                      = tcds;
78     flexio_spi_master_edma_private_handle_t *spiPrivateHandle = (flexio_spi_master_edma_private_handle_t *)param;
79 
80     /* Disable Tx DMA */
81     if (transferDone)
82     {
83         FLEXIO_SPI_EnableDMA(spiPrivateHandle->base, (uint32_t)kFLEXIO_SPI_TxDmaEnable, false);
84 
85         /* change the state */
86         spiPrivateHandle->handle->txInProgress = false;
87 
88         /* All finished, call the callback */
89         if ((spiPrivateHandle->handle->txInProgress == false) && (spiPrivateHandle->handle->rxInProgress == false))
90         {
91             if (spiPrivateHandle->handle->callback != NULL)
92             {
93                 (spiPrivateHandle->handle->callback)(spiPrivateHandle->base, spiPrivateHandle->handle, kStatus_Success,
94                                                      spiPrivateHandle->handle->userData);
95             }
96         }
97     }
98 }
99 
FLEXIO_SPI_RxEDMACallback(edma_handle_t * handle,void * param,bool transferDone,uint32_t tcds)100 static void FLEXIO_SPI_RxEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
101 {
102     tcds                                                      = tcds;
103     flexio_spi_master_edma_private_handle_t *spiPrivateHandle = (flexio_spi_master_edma_private_handle_t *)param;
104 
105     if (transferDone)
106     {
107         /* Disable Rx dma */
108         FLEXIO_SPI_EnableDMA(spiPrivateHandle->base, (uint32_t)kFLEXIO_SPI_RxDmaEnable, false);
109 
110         /* change the state */
111         spiPrivateHandle->handle->rxInProgress = false;
112 
113         /* All finished, call the callback */
114         if ((spiPrivateHandle->handle->txInProgress == false) && (spiPrivateHandle->handle->rxInProgress == false))
115         {
116             if (spiPrivateHandle->handle->callback != NULL)
117             {
118                 (spiPrivateHandle->handle->callback)(spiPrivateHandle->base, spiPrivateHandle->handle, kStatus_Success,
119                                                      spiPrivateHandle->handle->userData);
120             }
121         }
122     }
123 }
124 
FLEXIO_SPI_EDMAConfig(FLEXIO_SPI_Type * base,flexio_spi_master_edma_handle_t * handle,flexio_spi_transfer_t * xfer)125 static void FLEXIO_SPI_EDMAConfig(FLEXIO_SPI_Type *base,
126                                   flexio_spi_master_edma_handle_t *handle,
127                                   flexio_spi_transfer_t *xfer)
128 {
129     edma_transfer_config_t xferConfig      = {0};
130     flexio_spi_shift_direction_t direction = kFLEXIO_SPI_MsbFirst;
131     uint8_t bytesPerFrame;
132 
133     /* Configure the values in handle. */
134     switch (xfer->flags)
135     {
136         case (uint8_t)kFLEXIO_SPI_8bitMsb:
137             bytesPerFrame = 1U;
138             direction     = kFLEXIO_SPI_MsbFirst;
139             break;
140         case (uint8_t)kFLEXIO_SPI_8bitLsb:
141             bytesPerFrame = 1U;
142             direction     = kFLEXIO_SPI_LsbFirst;
143             break;
144         case (uint8_t)kFLEXIO_SPI_16bitMsb:
145             bytesPerFrame = 2U;
146             direction     = kFLEXIO_SPI_MsbFirst;
147             break;
148         case (uint8_t)kFLEXIO_SPI_16bitLsb:
149             bytesPerFrame = 2U;
150             direction     = kFLEXIO_SPI_LsbFirst;
151             break;
152         default:
153             bytesPerFrame = 1U;
154             direction     = kFLEXIO_SPI_MsbFirst;
155             assert(true);
156             break;
157     }
158 
159     /* Save total transfer size. */
160     handle->transferSize = xfer->dataSize;
161 
162     /* Configure tx transfer EDMA. */
163     xferConfig.destAddr   = FLEXIO_SPI_GetTxDataRegisterAddress(base, direction);
164     xferConfig.destOffset = 0;
165     if (bytesPerFrame == 1U)
166     {
167         xferConfig.srcTransferSize  = kEDMA_TransferSize1Bytes;
168         xferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
169         xferConfig.minorLoopBytes   = 1U;
170     }
171     else
172     {
173         if (direction == kFLEXIO_SPI_MsbFirst)
174         {
175             xferConfig.destAddr -= 1U;
176         }
177         xferConfig.srcTransferSize  = kEDMA_TransferSize2Bytes;
178         xferConfig.destTransferSize = kEDMA_TransferSize2Bytes;
179         xferConfig.minorLoopBytes   = 2U;
180     }
181 
182     /* Configure DMA channel. */
183     if (xfer->txData != NULL)
184     {
185         xferConfig.srcOffset = (int16_t)bytesPerFrame;
186         xferConfig.srcAddr   = (uint32_t)(xfer->txData);
187     }
188     else
189     {
190         /* Disable the source increasement and source set to dummyData. */
191         xferConfig.srcOffset = 0;
192         xferConfig.srcAddr   = (uint32_t)(&s_dummyData);
193     }
194 
195     xferConfig.majorLoopCounts = (xfer->dataSize / xferConfig.minorLoopBytes);
196 
197     /* Store the initially configured eDMA minor byte transfer count into the FLEXIO SPI handle */
198     handle->nbytes = (uint8_t)xferConfig.minorLoopBytes;
199 
200     if (handle->txHandle != NULL)
201     {
202         (void)EDMA_SubmitTransfer(handle->txHandle, &xferConfig);
203     }
204 
205     /* Configure rx transfer EDMA. */
206     if (xfer->rxData != NULL)
207     {
208         xferConfig.srcAddr = FLEXIO_SPI_GetRxDataRegisterAddress(base, direction);
209         if (bytesPerFrame == 2U)
210         {
211             if (direction == kFLEXIO_SPI_LsbFirst)
212             {
213                 xferConfig.srcAddr -= 1U;
214             }
215         }
216         xferConfig.srcOffset  = 0;
217         xferConfig.destAddr   = (uint32_t)(xfer->rxData);
218         xferConfig.destOffset = (int16_t)bytesPerFrame;
219         (void)EDMA_SubmitTransfer(handle->rxHandle, &xferConfig);
220         handle->rxInProgress = true;
221         FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_RxDmaEnable, true);
222         EDMA_StartTransfer(handle->rxHandle);
223     }
224 
225     /* Always start tx transfer. */
226     if (handle->txHandle != NULL)
227     {
228         handle->txInProgress = true;
229         FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_TxDmaEnable, true);
230         EDMA_StartTransfer(handle->txHandle);
231     }
232 }
233 
234 /*!
235  * brief Initializes the FlexIO SPI master eDMA handle.
236  *
237  * This function initializes the FlexIO SPI master eDMA handle which can be used for other FlexIO SPI master
238  * transactional
239  * APIs.
240  * For a specified FlexIO SPI instance, call this API once to get the initialized handle.
241  *
242  * param base Pointer to FLEXIO_SPI_Type structure.
243  * param handle Pointer to flexio_spi_master_edma_handle_t structure to store the transfer state.
244  * param callback SPI callback, NULL means no callback.
245  * param userData callback function parameter.
246  * param txHandle User requested eDMA handle for FlexIO SPI RX eDMA transfer.
247  * param rxHandle User requested eDMA handle for FlexIO SPI TX eDMA transfer.
248  * retval kStatus_Success Successfully create the handle.
249  * retval kStatus_OutOfRange The FlexIO SPI eDMA type/handle table out of range.
250  */
FLEXIO_SPI_MasterTransferCreateHandleEDMA(FLEXIO_SPI_Type * base,flexio_spi_master_edma_handle_t * handle,flexio_spi_master_edma_transfer_callback_t callback,void * userData,edma_handle_t * txHandle,edma_handle_t * rxHandle)251 status_t FLEXIO_SPI_MasterTransferCreateHandleEDMA(FLEXIO_SPI_Type *base,
252                                                    flexio_spi_master_edma_handle_t *handle,
253                                                    flexio_spi_master_edma_transfer_callback_t callback,
254                                                    void *userData,
255                                                    edma_handle_t *txHandle,
256                                                    edma_handle_t *rxHandle)
257 {
258     assert(handle != NULL);
259 
260     uint8_t index = 0;
261 
262     /* Find the an empty handle pointer to store the handle. */
263     for (index = 0U; index < (uint8_t)FLEXIO_SPI_HANDLE_COUNT; index++)
264     {
265         if (s_edmaPrivateHandle[index].base == NULL)
266         {
267             s_edmaPrivateHandle[index].base   = base;
268             s_edmaPrivateHandle[index].handle = handle;
269             break;
270         }
271     }
272 
273     if (index == (uint16_t)FLEXIO_SPI_HANDLE_COUNT)
274     {
275         return kStatus_OutOfRange;
276     }
277 
278     /* Set spi base to handle. */
279     handle->txHandle = txHandle;
280     handle->rxHandle = rxHandle;
281 
282     /* Register callback and userData. */
283     handle->callback = callback;
284     handle->userData = userData;
285 
286     /* Set SPI state to idle. */
287     handle->txInProgress = false;
288     handle->rxInProgress = false;
289 
290     /* Install callback for Tx/Rx dma channel. */
291     if (handle->txHandle != NULL)
292     {
293         EDMA_SetCallback(handle->txHandle, FLEXIO_SPI_TxEDMACallback, &s_edmaPrivateHandle[index]);
294     }
295     if (handle->rxHandle != NULL)
296     {
297         EDMA_SetCallback(handle->rxHandle, FLEXIO_SPI_RxEDMACallback, &s_edmaPrivateHandle[index]);
298     }
299 
300     return kStatus_Success;
301 }
302 
303 /*!
304  * brief Performs a non-blocking FlexIO SPI transfer using eDMA.
305  *
306  * note This interface returns immediately after transfer initiates. Call
307  * FLEXIO_SPI_MasterGetTransferCountEDMA to poll the transfer status and check
308  * whether the FlexIO SPI transfer is finished.
309  *
310  * param base Pointer to FLEXIO_SPI_Type structure.
311  * param handle Pointer to flexio_spi_master_edma_handle_t structure to store the transfer state.
312  * param xfer Pointer to FlexIO SPI transfer structure.
313  * retval kStatus_Success Successfully start a transfer.
314  * retval kStatus_InvalidArgument Input argument is invalid.
315  * retval kStatus_FLEXIO_SPI_Busy FlexIO SPI is not idle, is running another transfer.
316  */
FLEXIO_SPI_MasterTransferEDMA(FLEXIO_SPI_Type * base,flexio_spi_master_edma_handle_t * handle,flexio_spi_transfer_t * xfer)317 status_t FLEXIO_SPI_MasterTransferEDMA(FLEXIO_SPI_Type *base,
318                                        flexio_spi_master_edma_handle_t *handle,
319                                        flexio_spi_transfer_t *xfer)
320 {
321     assert(handle != NULL);
322     assert(xfer != NULL);
323 
324     uint32_t dataMode = 0;
325     uint16_t timerCmp = (uint16_t)base->flexioBase->TIMCMP[base->timerIndex[0]];
326 
327     timerCmp &= 0x00FFU;
328 
329     /* Check if the device is busy. */
330     if ((handle->txInProgress) || (handle->rxInProgress))
331     {
332         return kStatus_FLEXIO_SPI_Busy;
333     }
334 
335     /* Check if input parameter invalid. */
336     if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U))
337     {
338         return kStatus_InvalidArgument;
339     }
340 
341     /* configure data mode. */
342     if ((xfer->flags == (uint8_t)kFLEXIO_SPI_8bitMsb) || (xfer->flags == (uint8_t)kFLEXIO_SPI_8bitLsb))
343     {
344         dataMode = (8UL * 2UL - 1UL) << 8U;
345     }
346     else if ((xfer->flags == (uint8_t)kFLEXIO_SPI_16bitMsb) || (xfer->flags == (uint8_t)kFLEXIO_SPI_16bitLsb))
347     {
348         dataMode = (16UL * 2UL - 1UL) << 8U;
349     }
350     else
351     {
352         dataMode = 8UL * 2UL - 1UL;
353     }
354 
355     dataMode |= timerCmp;
356 
357     base->flexioBase->TIMCMP[base->timerIndex[0]] = dataMode;
358 
359     FLEXIO_SPI_EDMAConfig(base, handle, xfer);
360 
361     return kStatus_Success;
362 }
363 
364 /*!
365  * brief Gets the remaining bytes for FlexIO SPI eDMA transfer.
366  *
367  * param base Pointer to FLEXIO_SPI_Type structure.
368  * param handle FlexIO SPI eDMA handle pointer.
369  * param count Number of bytes transferred so far by the non-blocking transaction.
370  */
FLEXIO_SPI_MasterTransferGetCountEDMA(FLEXIO_SPI_Type * base,flexio_spi_master_edma_handle_t * handle,size_t * count)371 status_t FLEXIO_SPI_MasterTransferGetCountEDMA(FLEXIO_SPI_Type *base,
372                                                flexio_spi_master_edma_handle_t *handle,
373                                                size_t *count)
374 {
375     assert(handle != NULL);
376 
377     if (NULL == count)
378     {
379         return kStatus_InvalidArgument;
380     }
381 
382     if (handle->rxInProgress)
383     {
384         *count =
385             (handle->transferSize - (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(
386                                                                    handle->rxHandle->base, handle->rxHandle->channel));
387     }
388     else
389     {
390         *count =
391             (handle->transferSize - (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(
392                                                                    handle->txHandle->base, handle->txHandle->channel));
393     }
394 
395     return kStatus_Success;
396 }
397 
398 /*!
399  * brief Aborts a FlexIO SPI transfer using eDMA.
400  *
401  * param base Pointer to FLEXIO_SPI_Type structure.
402  * param handle FlexIO SPI eDMA handle pointer.
403  */
FLEXIO_SPI_MasterTransferAbortEDMA(FLEXIO_SPI_Type * base,flexio_spi_master_edma_handle_t * handle)404 void FLEXIO_SPI_MasterTransferAbortEDMA(FLEXIO_SPI_Type *base, flexio_spi_master_edma_handle_t *handle)
405 {
406     assert(handle != NULL);
407 
408     /* Disable dma. */
409     EDMA_StopTransfer(handle->txHandle);
410     EDMA_StopTransfer(handle->rxHandle);
411 
412     /* Disable DMA enable bit. */
413     FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_DmaAllEnable, false);
414 
415     /* Set the handle state. */
416     handle->txInProgress = false;
417     handle->rxInProgress = false;
418 }
419 
420 /*!
421  * brief Performs a non-blocking FlexIO SPI transfer using eDMA.
422  *
423  * note This interface returns immediately after transfer initiates. Call
424  * FLEXIO_SPI_SlaveGetTransferCountEDMA to poll the transfer status and
425  * check whether the FlexIO SPI transfer is finished.
426  *
427  * param base Pointer to FLEXIO_SPI_Type structure.
428  * param handle Pointer to flexio_spi_slave_edma_handle_t structure to store the transfer state.
429  * param xfer Pointer to FlexIO SPI transfer structure.
430  * retval kStatus_Success Successfully start a transfer.
431  * retval kStatus_InvalidArgument Input argument is invalid.
432  * retval kStatus_FLEXIO_SPI_Busy FlexIO SPI is not idle, is running another transfer.
433  */
FLEXIO_SPI_SlaveTransferEDMA(FLEXIO_SPI_Type * base,flexio_spi_slave_edma_handle_t * handle,flexio_spi_transfer_t * xfer)434 status_t FLEXIO_SPI_SlaveTransferEDMA(FLEXIO_SPI_Type *base,
435                                       flexio_spi_slave_edma_handle_t *handle,
436                                       flexio_spi_transfer_t *xfer)
437 {
438     assert(handle != NULL);
439     assert(xfer != NULL);
440 
441     uint32_t dataMode = 0U;
442 
443     /* Check if the device is busy. */
444     if ((handle->txInProgress) || (handle->rxInProgress))
445     {
446         return kStatus_FLEXIO_SPI_Busy;
447     }
448 
449     /* Check if input parameter invalid. */
450     if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U))
451     {
452         return kStatus_InvalidArgument;
453     }
454 
455     /* configure data mode. */
456     if ((xfer->flags == (uint8_t)kFLEXIO_SPI_8bitMsb) || (xfer->flags == (uint8_t)kFLEXIO_SPI_8bitLsb))
457     {
458         dataMode = 8U * 2U - 1U;
459     }
460     else if ((xfer->flags == (uint8_t)kFLEXIO_SPI_16bitMsb) || (xfer->flags == (uint8_t)kFLEXIO_SPI_16bitLsb))
461     {
462         dataMode = 16U * 2U - 1U;
463     }
464     else
465     {
466         dataMode = 8U * 2U - 1U;
467     }
468 
469     base->flexioBase->TIMCMP[base->timerIndex[0]] = dataMode;
470 
471     FLEXIO_SPI_EDMAConfig(base, handle, xfer);
472 
473     return kStatus_Success;
474 }
475