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