1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017, 2020 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_spi_dma.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.spi_dma"
18 #endif
19 
20 /*<! Structure definition for spi_dma_private_handle_t. The structure is private. */
21 typedef struct _spi_dma_private_handle
22 {
23     SPI_Type *base;
24     spi_dma_handle_t *handle;
25 } spi_dma_private_handle_t;
26 
27 /*! @brief SPI transfer state, which is used for SPI transactiaonl APIs' internal state. */
28 enum _spi_dma_states_t
29 {
30     kSPI_Idle = 0x0, /*!< SPI is idle state */
31     kSPI_Busy        /*!< SPI is busy tranferring data. */
32 };
33 
34 /*<! Private handle only used for internally. */
35 static spi_dma_private_handle_t s_dmaPrivateHandle[FSL_FEATURE_SOC_SPI_COUNT];
36 
37 /* If the RX data is not required, save it to this variable. */
38 static uint16_t s_spiDmaRxDrop;
39 /*******************************************************************************
40  * Prototypes
41  ******************************************************************************/
42 /*!
43  * @brief DMA callback function for SPI send transfer.
44  *
45  * @param handle DMA handle pointer.
46  * @param userData User data for DMA callback function.
47  */
48 static void SPI_TxDMACallback(dma_handle_t *handle, void *userData);
49 
50 /*!
51  * @brief DMA callback function for SPI receive transfer.
52  *
53  * @param handle DMA handle pointer.
54  * @param userData User data for DMA callback function.
55  */
56 static void SPI_RxDMACallback(dma_handle_t *handle, void *userData);
57 
58 /*******************************************************************************
59  * Code
60  ******************************************************************************/
SPI_TxDMACallback(dma_handle_t * handle,void * userData)61 static void SPI_TxDMACallback(dma_handle_t *handle, void *userData)
62 {
63     spi_dma_private_handle_t *privHandle = (spi_dma_private_handle_t *)userData;
64     spi_dma_handle_t *spiHandle          = privHandle->handle;
65     SPI_Type *base                       = privHandle->base;
66 
67     /* Disable Tx dma */
68     SPI_EnableDMA(base, (uint8_t)kSPI_TxDmaEnable, false);
69 
70     /* Stop DMA transfer */
71     DMA_StopTransfer(spiHandle->txHandle);
72 
73     /* change the state */
74     spiHandle->txInProgress = false;
75 
76     /* All finished, call the callback */
77     if ((spiHandle->txInProgress == false) && (spiHandle->rxInProgress == false))
78     {
79         spiHandle->state = (uint32_t)kSPI_Idle;
80         if (spiHandle->callback != NULL)
81         {
82             (spiHandle->callback)(base, spiHandle, kStatus_Success, spiHandle->userData);
83         }
84     }
85 }
86 
SPI_RxDMACallback(dma_handle_t * handle,void * userData)87 static void SPI_RxDMACallback(dma_handle_t *handle, void *userData)
88 {
89     spi_dma_private_handle_t *privHandle = (spi_dma_private_handle_t *)userData;
90     spi_dma_handle_t *spiHandle          = privHandle->handle;
91     SPI_Type *base                       = privHandle->base;
92 
93     /* Disable Tx dma */
94     SPI_EnableDMA(base, (uint8_t)kSPI_RxDmaEnable, false);
95 
96     /* Stop DMA transfer */
97     DMA_StopTransfer(spiHandle->rxHandle);
98 
99     /* change the state */
100     spiHandle->rxInProgress = false;
101 
102     /* All finished, call the callback */
103     if ((spiHandle->txInProgress == false) && (spiHandle->rxInProgress == false))
104     {
105         spiHandle->state = (uint32_t)kSPI_Idle;
106         if (spiHandle->callback != NULL)
107         {
108             (spiHandle->callback)(base, spiHandle, kStatus_Success, spiHandle->userData);
109         }
110     }
111 }
112 
113 /*!
114  * brief Initialize the SPI master DMA handle.
115  *
116  * This function initializes the SPI master DMA handle which can be used for other SPI master transactional APIs.
117  * Usually, for a specified SPI instance, user need only call this API once to get the initialized handle.
118  *
119  * param base SPI peripheral base address.
120  * param handle SPI handle pointer.
121  * param callback User callback function called at the end of a transfer.
122  * param userData User data for callback.
123  * param txHandle DMA handle pointer for SPI Tx, the handle shall be static allocated by users.
124  * param rxHandle DMA handle pointer for SPI Rx, the handle shall be static allocated by users.
125  */
SPI_MasterTransferCreateHandleDMA(SPI_Type * base,spi_dma_handle_t * handle,spi_dma_callback_t callback,void * userData,dma_handle_t * txHandle,dma_handle_t * rxHandle)126 void SPI_MasterTransferCreateHandleDMA(SPI_Type *base,
127                                        spi_dma_handle_t *handle,
128                                        spi_dma_callback_t callback,
129                                        void *userData,
130                                        dma_handle_t *txHandle,
131                                        dma_handle_t *rxHandle)
132 {
133     assert(handle != NULL);
134     uint32_t instance = SPI_GetInstance(base);
135 
136     /* Zero the handle */
137     (void)memset(handle, 0, sizeof(*handle));
138 
139     /* Set spi base to handle */
140     handle->txHandle = txHandle;
141     handle->rxHandle = rxHandle;
142     handle->callback = callback;
143     handle->userData = userData;
144 
145     /* Set SPI state to idle */
146     handle->state = (uint32_t)kSPI_Idle;
147 
148     /* Set handle to global state */
149     s_dmaPrivateHandle[instance].base   = base;
150     s_dmaPrivateHandle[instance].handle = handle;
151 
152 /* Compute internal state */
153 #if defined(FSL_FEATURE_SPI_16BIT_TRANSFERS) && (FSL_FEATURE_SPI_16BIT_TRANSFERS)
154     handle->bytesPerFrame = ((base->C2 & SPI_C2_SPIMODE_MASK) >> SPI_C2_SPIMODE_SHIFT) + 1U;
155 #else
156     handle->bytesPerFrame = 1U;
157 #endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */
158 
159 #if defined(FSL_FEATURE_SPI_HAS_FIFO) && (FSL_FEATURE_SPI_HAS_FIFO)
160     /* If using DMA, disable FIFO, as the FIFO may cause data loss if the data size is not integer
161        times of 2bytes. As SPI cannot set watermark to 0, only can set to 1/2 FIFO size or 3/4 FIFO
162        size. */
163     if (FSL_FEATURE_SPI_FIFO_SIZEn(base) != 0)
164     {
165         base->C3 &= (uint8_t)(~SPI_C3_FIFOMODE_MASK);
166     }
167 
168 #endif /* FSL_FEATURE_SPI_HAS_FIFO */
169 
170     /* Install callback for Tx dma channel */
171     DMA_SetCallback(handle->txHandle, SPI_TxDMACallback, &s_dmaPrivateHandle[instance]);
172     DMA_SetCallback(handle->rxHandle, SPI_RxDMACallback, &s_dmaPrivateHandle[instance]);
173 }
174 
175 /*!
176  * brief Perform a non-blocking SPI transfer using DMA.
177  *
178  * note This interface returned immediately after transfer initiates, users should call
179  * SPI_GetTransferStatus to poll the transfer status to check whether SPI transfer finished.
180  *
181  * param base SPI peripheral base address.
182  * param handle SPI DMA handle pointer.
183  * param xfer Pointer to dma transfer structure.
184  * retval kStatus_Success Successfully start a transfer.
185  * retval kStatus_InvalidArgument Input argument is invalid.
186  * retval kStatus_SPI_Busy SPI is not idle, is running another transfer.
187  */
SPI_MasterTransferDMA(SPI_Type * base,spi_dma_handle_t * handle,spi_transfer_t * xfer)188 status_t SPI_MasterTransferDMA(SPI_Type *base, spi_dma_handle_t *handle, spi_transfer_t *xfer)
189 {
190     assert((handle != NULL) && (xfer != NULL));
191 
192     dma_transfer_config_t config = {0};
193 
194     /* Check if the device is busy */
195     if (handle->state == (uint32_t)kSPI_Busy)
196     {
197         return (status_t)kStatus_SPI_Busy;
198     }
199 
200     /* Check if input parameter invalid */
201     if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U))
202     {
203         return (status_t)kStatus_InvalidArgument;
204     }
205 
206     /* Disable SPI and then enable it, this is used to clear S register*/
207     SPI_Enable(base, false);
208     SPI_Enable(base, true);
209 
210     /* Configure DMA transfer. */
211     if (handle->bytesPerFrame == 1U)
212     {
213         config.srcSize  = kDMA_Transfersize8bits;
214         config.destSize = kDMA_Transfersize8bits;
215     }
216     else
217     {
218         config.srcSize  = kDMA_Transfersize16bits;
219         config.destSize = kDMA_Transfersize16bits;
220     }
221     config.transferSize = xfer->dataSize;
222 
223     /* Configure tx transfer DMA */
224     config.destAddr            = SPI_GetDataRegisterAddress(base);
225     config.enableDestIncrement = false;
226     /* Configure DMA channel */
227     if (xfer->txData != NULL)
228     {
229         config.enableSrcIncrement = true;
230         config.srcAddr            = (uint32_t)(xfer->txData);
231     }
232     else
233     {
234         /* Disable the source increasement and source set to dummyData */
235         config.enableSrcIncrement = false;
236         config.srcAddr            = (uint32_t)(&g_spiDummyData[SPI_GetInstance(base)]);
237     }
238     (void)DMA_SubmitTransfer(handle->txHandle, &config, 1U);
239 
240     /*
241      * Configure rx transfer DMA.
242      * To make sure TX data has been sent out to bus, SPI DMA driver
243      * checks the RX data count. When the RX data reaches the
244      * desired count, it means TX data has been sent out to bus.
245      * So DMA RX is enabled even when RX data is not desired,
246      * and the data is saved to variable and dropped.
247      */
248     config.srcAddr            = SPI_GetDataRegisterAddress(base);
249     config.enableSrcIncrement = false;
250 
251     if (xfer->rxData != NULL)
252     {
253         config.destAddr            = (uint32_t)(xfer->rxData);
254         config.enableDestIncrement = true;
255     }
256     else
257     {
258         config.destAddr            = (uint32_t)(&s_spiDmaRxDrop);
259         config.enableDestIncrement = false;
260     }
261     (void)DMA_SubmitTransfer(handle->rxHandle, &config, (uint32_t)kDMA_EnableInterrupt);
262 
263     /* Change the state of handle */
264     handle->transferSize = xfer->dataSize;
265     handle->state        = (uint32_t)kSPI_Busy;
266 
267     /* Start Rx transfer */
268     handle->rxInProgress = true;
269     SPI_EnableDMA(base, (uint8_t)kSPI_RxDmaEnable, true);
270     DMA_StartTransfer(handle->rxHandle);
271 
272     /* Always start Tx transfer */
273     handle->txInProgress = true;
274     SPI_EnableDMA(base, (uint8_t)kSPI_TxDmaEnable, true);
275     DMA_StartTransfer(handle->txHandle);
276 
277     return kStatus_Success;
278 }
279 
280 /*!
281  * brief Get the transferred bytes for SPI slave DMA.
282  *
283  * param base SPI peripheral base address.
284  * param handle SPI DMA handle pointer.
285  * param count Transferred bytes.
286  * retval kStatus_SPI_Success Succeed get the transfer count.
287  * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress.
288  */
SPI_MasterTransferGetCountDMA(SPI_Type * base,spi_dma_handle_t * handle,size_t * count)289 status_t SPI_MasterTransferGetCountDMA(SPI_Type *base, spi_dma_handle_t *handle, size_t *count)
290 {
291     assert(handle != NULL);
292 
293     status_t status = kStatus_Success;
294 
295     if (handle->state != (uint32_t)kSPI_Busy)
296     {
297         status = kStatus_NoTransferInProgress;
298     }
299     else
300     {
301         if (handle->rxInProgress)
302         {
303             *count = handle->transferSize - DMA_GetRemainingBytes(handle->rxHandle->base, handle->rxHandle->channel);
304         }
305         else
306         {
307             *count = handle->transferSize - DMA_GetRemainingBytes(handle->txHandle->base, handle->txHandle->channel);
308         }
309     }
310 
311     return status;
312 }
313 
314 /*!
315  * brief Abort a SPI transfer using DMA.
316  *
317  * param base SPI peripheral base address.
318  * param handle SPI DMA handle pointer.
319  */
SPI_MasterTransferAbortDMA(SPI_Type * base,spi_dma_handle_t * handle)320 void SPI_MasterTransferAbortDMA(SPI_Type *base, spi_dma_handle_t *handle)
321 {
322     assert(handle != NULL);
323 
324     /* Disable dma */
325     DMA_StopTransfer(handle->txHandle);
326     DMA_StopTransfer(handle->rxHandle);
327 
328     /* Disable DMA enable bit */
329     SPI_EnableDMA(base, (uint8_t)kSPI_DmaAllEnable, false);
330 
331     /* Set the handle state */
332     handle->txInProgress = false;
333     handle->rxInProgress = false;
334     handle->state        = (uint32_t)kSPI_Idle;
335 }
336