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