1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2020, 2022 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_flexio_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.flexio_spi_dma"
18 #endif
19
20 /*<! Structure definition for spi_dma_private_handle_t. The structure is private. */
21 typedef struct _flexio_spi_master_dma_private_handle
22 {
23 FLEXIO_SPI_Type *base;
24 flexio_spi_master_dma_handle_t *handle;
25 } flexio_spi_master_dma_private_handle_t;
26
27 /*******************************************************************************
28 * Prototypes
29 ******************************************************************************/
30
31 /*!
32 * @brief DMA callback function for FLEXIO SPI send transfer.
33 *
34 * @param handle DMA handle pointer.
35 * @param param Callback function parameter.
36 */
37 static void FLEXIO_SPI_TxDMACallback(dma_handle_t *handle, void *param);
38
39 /*!
40 * @brief DMA callback function for FLEXIO SPI receive transfer.
41 *
42 * @param handle DMA handle pointer.
43 * @param param Callback function parameter.
44 */
45 static void FLEXIO_SPI_RxDMACallback(dma_handle_t *handle, void *param);
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_dma_handle_t structure to store the transfer state.
52 * @param xfer Pointer to flexio spi transfer structure.
53 * @retval kStatus_Success Successfully create the handle.
54 * @retval kStatus_InvalidArgument The transfer size is not supported.
55 */
56 static status_t FLEXIO_SPI_DMAConfig(FLEXIO_SPI_Type *base,
57 flexio_spi_master_dma_handle_t *handle,
58 flexio_spi_transfer_t *xfer);
59
60 /*******************************************************************************
61 * Variables
62 ******************************************************************************/
63
64 /* Dummy data used to send */
65 static const uint32_t s_dummyData = FLEXIO_SPI_DUMMYDATA;
66
67 /*< @brief user configurable flexio spi handle count. */
68 #define FLEXIO_SPI_HANDLE_COUNT 2
69
70 /*<! Private handle only used for internally. */
71 static flexio_spi_master_dma_private_handle_t s_dmaPrivateHandle[FLEXIO_SPI_HANDLE_COUNT];
72
73 /*******************************************************************************
74 * Code
75 ******************************************************************************/
76
FLEXIO_SPI_TxDMACallback(dma_handle_t * handle,void * param)77 static void FLEXIO_SPI_TxDMACallback(dma_handle_t *handle, void *param)
78 {
79 flexio_spi_master_dma_private_handle_t *spiPrivateHandle = (flexio_spi_master_dma_private_handle_t *)param;
80
81 /* Disable Tx DMA. */
82 FLEXIO_SPI_EnableDMA(spiPrivateHandle->base, (uint32_t)kFLEXIO_SPI_TxDmaEnable, false);
83
84 /* Disable interrupt. */
85 DMA_DisableInterrupts(handle->base, handle->channel);
86
87 /* change the state. */
88 spiPrivateHandle->handle->txInProgress = false;
89
90 /* All finished, call the callback. */
91 if ((spiPrivateHandle->handle->txInProgress == false) && (spiPrivateHandle->handle->rxInProgress == false))
92 {
93 if (spiPrivateHandle->handle->callback != NULL)
94 {
95 (spiPrivateHandle->handle->callback)(spiPrivateHandle->base, spiPrivateHandle->handle, kStatus_Success,
96 spiPrivateHandle->handle->userData);
97 }
98 }
99 }
100
FLEXIO_SPI_RxDMACallback(dma_handle_t * handle,void * param)101 static void FLEXIO_SPI_RxDMACallback(dma_handle_t *handle, void *param)
102 {
103 flexio_spi_master_dma_private_handle_t *spiPrivateHandle = (flexio_spi_master_dma_private_handle_t *)param;
104
105 /* Disable Rx DMA. */
106 FLEXIO_SPI_EnableDMA(spiPrivateHandle->base, (uint32_t)kFLEXIO_SPI_RxDmaEnable, false);
107
108 /* Disable interrupt. */
109 DMA_DisableInterrupts(handle->base, handle->channel);
110
111 /* change the state. */
112 spiPrivateHandle->handle->rxInProgress = false;
113
114 /* All finished, call the callback. */
115 if ((spiPrivateHandle->handle->txInProgress == false) && (spiPrivateHandle->handle->rxInProgress == false))
116 {
117 if (spiPrivateHandle->handle->callback != NULL)
118 {
119 (spiPrivateHandle->handle->callback)(spiPrivateHandle->base, spiPrivateHandle->handle, kStatus_Success,
120 spiPrivateHandle->handle->userData);
121 }
122 }
123 }
124
FLEXIO_SPI_DMAConfig(FLEXIO_SPI_Type * base,flexio_spi_master_dma_handle_t * handle,flexio_spi_transfer_t * xfer)125 static status_t FLEXIO_SPI_DMAConfig(FLEXIO_SPI_Type *base,
126 flexio_spi_master_dma_handle_t *handle,
127 flexio_spi_transfer_t *xfer)
128 {
129 dma_transfer_config_t xferConfig = {0};
130 flexio_spi_shift_direction_t direction = kFLEXIO_SPI_MsbFirst;
131 uint8_t bytesPerFrame;
132 uint8_t dataFormat = FLEXIO_SPI_XFER_DATA_FORMAT(xfer->flags);
133
134 /* Configure the values in handle. */
135 switch (dataFormat)
136 {
137 case (uint8_t)kFLEXIO_SPI_8bitMsb:
138 bytesPerFrame = 1U;
139 direction = kFLEXIO_SPI_MsbFirst;
140 break;
141 case (uint8_t)kFLEXIO_SPI_8bitLsb:
142 bytesPerFrame = 1U;
143 direction = kFLEXIO_SPI_LsbFirst;
144 break;
145 case (uint8_t)kFLEXIO_SPI_16bitMsb:
146 bytesPerFrame = 2U;
147 direction = kFLEXIO_SPI_MsbFirst;
148 break;
149 case (uint8_t)kFLEXIO_SPI_16bitLsb:
150 bytesPerFrame = 2U;
151 direction = kFLEXIO_SPI_LsbFirst;
152 break;
153 case (uint8_t)kFLEXIO_SPI_32bitMsb:
154 bytesPerFrame = 4U;
155 direction = kFLEXIO_SPI_MsbFirst;
156 break;
157 case (uint8_t)kFLEXIO_SPI_32bitLsb:
158 bytesPerFrame = 4U;
159 direction = kFLEXIO_SPI_LsbFirst;
160 break;
161 default:
162 bytesPerFrame = 1U;
163 direction = kFLEXIO_SPI_MsbFirst;
164 assert(true);
165 break;
166 }
167
168 /* Transfer size should be bytesPerFrame divisible. */
169 if ((xfer->dataSize % bytesPerFrame) != 0U)
170 {
171 return kStatus_InvalidArgument;
172 }
173
174 /* Save total transfer size. */
175 handle->transferSize = xfer->dataSize;
176
177 /* Configure tx transfer DMA. */
178 xferConfig.destAddr = FLEXIO_SPI_GetTxDataRegisterAddress(base, direction);
179 xferConfig.enableDestIncrement = false;
180 if (bytesPerFrame == 1U)
181 {
182 xferConfig.srcSize = kDMA_Transfersize8bits;
183 xferConfig.destSize = kDMA_Transfersize8bits;
184 }
185 else if (bytesPerFrame == 2U)
186 {
187 if (direction == kFLEXIO_SPI_MsbFirst)
188 {
189 xferConfig.destAddr -= 1U;
190 }
191 xferConfig.srcSize = kDMA_Transfersize16bits;
192 xferConfig.destSize = kDMA_Transfersize16bits;
193 }
194 else
195 {
196 if (direction == kFLEXIO_SPI_MsbFirst)
197 {
198 xferConfig.destAddr -= 3U;
199 }
200 xferConfig.srcSize = kDMA_Transfersize32bits;
201 xferConfig.destSize = kDMA_Transfersize32bits;
202 }
203
204 /* Configure DMA channel. */
205 if (xfer->txData != NULL)
206 {
207 xferConfig.enableSrcIncrement = true;
208 xferConfig.srcAddr = (uint32_t)(xfer->txData);
209 }
210 else
211 {
212 /* Disable the source increasement and source set to dummyData. */
213 xferConfig.enableSrcIncrement = false;
214 xferConfig.srcAddr = (uint32_t)(&s_dummyData);
215 }
216
217 xferConfig.transferSize = xfer->dataSize;
218
219 if (handle->txHandle != NULL)
220 {
221 (void)DMA_SubmitTransfer(handle->txHandle, &xferConfig, (uint32_t)kDMA_EnableInterrupt);
222 }
223
224 /* Configure tx transfer DMA. */
225 if (xfer->rxData != NULL)
226 {
227 xferConfig.srcAddr = FLEXIO_SPI_GetRxDataRegisterAddress(base, direction);
228 xferConfig.enableSrcIncrement = false;
229 xferConfig.destAddr = (uint32_t)(xfer->rxData);
230 xferConfig.enableDestIncrement = true;
231 (void)DMA_SubmitTransfer(handle->rxHandle, &xferConfig, (uint32_t)kDMA_EnableInterrupt);
232 handle->rxInProgress = true;
233 FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_RxDmaEnable, true);
234 DMA_StartTransfer(handle->rxHandle);
235 }
236
237 /* Always start Tx transfer. */
238 if (handle->txHandle != NULL)
239 {
240 handle->txInProgress = true;
241 FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_TxDmaEnable, true);
242 DMA_StartTransfer(handle->txHandle);
243 }
244
245 return kStatus_Success;
246 }
247
248 /*!
249 * brief Initializes the FLEXO SPI master DMA handle.
250 *
251 * This function initializes the FLEXO SPI master DMA handle which can be used for other FLEXO SPI master transactional
252 * APIs.
253 * Usually, for a specified FLEXO SPI instance, call this API once to get the initialized handle.
254 *
255 * param base Pointer to FLEXIO_SPI_Type structure.
256 * param handle Pointer to flexio_spi_master_dma_handle_t structure to store the transfer state.
257 * param callback SPI callback, NULL means no callback.
258 * param userData callback function parameter.
259 * param txHandle User requested DMA handle for FlexIO SPI RX DMA transfer.
260 * param rxHandle User requested DMA handle for FlexIO SPI TX DMA transfer.
261 * retval kStatus_Success Successfully create the handle.
262 * retval kStatus_OutOfRange The FlexIO SPI DMA type/handle table out of range.
263 */
FLEXIO_SPI_MasterTransferCreateHandleDMA(FLEXIO_SPI_Type * base,flexio_spi_master_dma_handle_t * handle,flexio_spi_master_dma_transfer_callback_t callback,void * userData,dma_handle_t * txHandle,dma_handle_t * rxHandle)264 status_t FLEXIO_SPI_MasterTransferCreateHandleDMA(FLEXIO_SPI_Type *base,
265 flexio_spi_master_dma_handle_t *handle,
266 flexio_spi_master_dma_transfer_callback_t callback,
267 void *userData,
268 dma_handle_t *txHandle,
269 dma_handle_t *rxHandle)
270 {
271 assert(handle != NULL);
272
273 uint8_t index = 0;
274
275 /* Find the an empty handle pointer to store the handle. */
276 for (index = 0U; index < (uint8_t)FLEXIO_SPI_HANDLE_COUNT; index++)
277 {
278 if (s_dmaPrivateHandle[index].base == NULL)
279 {
280 s_dmaPrivateHandle[index].base = base;
281 s_dmaPrivateHandle[index].handle = handle;
282 break;
283 }
284 }
285
286 if (index == (uint8_t)FLEXIO_SPI_HANDLE_COUNT)
287 {
288 return kStatus_OutOfRange;
289 }
290
291 /* Set spi base to handle. */
292 handle->txHandle = txHandle;
293 handle->rxHandle = rxHandle;
294
295 /* Register callback and userData. */
296 handle->callback = callback;
297 handle->userData = userData;
298
299 /* Set SPI state to idle. */
300 handle->txInProgress = false;
301 handle->rxInProgress = false;
302
303 /* Install callback for Tx/Rx dma channel. */
304 if (handle->txHandle != NULL)
305 {
306 DMA_SetCallback(handle->txHandle, FLEXIO_SPI_TxDMACallback, &s_dmaPrivateHandle[index]);
307 }
308 if (handle->rxHandle != NULL)
309 {
310 DMA_SetCallback(handle->rxHandle, FLEXIO_SPI_RxDMACallback, &s_dmaPrivateHandle[index]);
311 }
312
313 return kStatus_Success;
314 }
315
316 /*!
317 * brief Performs a non-blocking FlexIO SPI transfer using DMA.
318 *
319 * note This interface returned immediately after transfer initiates. Call
320 * FLEXIO_SPI_MasterGetTransferCountDMA to poll the transfer status to check
321 * whether the FlexIO SPI transfer is finished.
322 *
323 * param base Pointer to FLEXIO_SPI_Type structure.
324 * param handle Pointer to flexio_spi_master_dma_handle_t structure to store the transfer state.
325 * param xfer Pointer to FlexIO SPI transfer structure.
326 * retval kStatus_Success Successfully start a transfer.
327 * retval kStatus_InvalidArgument Input argument is invalid.
328 * retval kStatus_FLEXIO_SPI_Busy FlexIO SPI is not idle, is running another transfer.
329 */
FLEXIO_SPI_MasterTransferDMA(FLEXIO_SPI_Type * base,flexio_spi_master_dma_handle_t * handle,flexio_spi_transfer_t * xfer)330 status_t FLEXIO_SPI_MasterTransferDMA(FLEXIO_SPI_Type *base,
331 flexio_spi_master_dma_handle_t *handle,
332 flexio_spi_transfer_t *xfer)
333 {
334 assert(handle != NULL);
335 assert(xfer != NULL);
336
337 uint32_t dataMode = 0U;
338 uint16_t timerCmp = (uint16_t)base->flexioBase->TIMCMP[base->timerIndex[0]];
339 uint8_t dataFormat = FLEXIO_SPI_XFER_DATA_FORMAT(xfer->flags);
340
341 timerCmp &= 0x00FFU;
342
343 /* Check if the device is busy. */
344 if ((handle->txInProgress) || (handle->rxInProgress))
345 {
346 return kStatus_FLEXIO_SPI_Busy;
347 }
348
349 /* Check if input parameter invalid. */
350 if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U))
351 {
352 return kStatus_InvalidArgument;
353 }
354
355 /* Timer1 controls the CS signal which enables/disables(asserts/deasserts) when timer0 enable/disable. Timer0
356 enables when tx shifter is written and disables when timer compare. The timer compare event causes the
357 transmit shift registers to load which generates a tx register empty event. Since when timer stop bit is
358 disabled, a timer enable condition can be detected in the same cycle as a timer disable condition, so if
359 software writes the tx register upon the detection of tx register empty event, the timer enable condition
360 is triggered again, then the CS signal can remain low until software no longer writes the tx register. */
361 if ((xfer->flags & (uint8_t)kFLEXIO_SPI_csContinuous) != 0U)
362 {
363 base->flexioBase->TIMCFG[base->timerIndex[0]] =
364 (base->flexioBase->TIMCFG[base->timerIndex[0]] & ~FLEXIO_TIMCFG_TSTOP_MASK) |
365 FLEXIO_TIMCFG_TSTOP(kFLEXIO_TimerStopBitDisabled);
366 }
367 else
368 {
369 base->flexioBase->TIMCFG[base->timerIndex[0]] =
370 (base->flexioBase->TIMCFG[base->timerIndex[0]] & ~FLEXIO_TIMCFG_TSTOP_MASK) |
371 FLEXIO_TIMCFG_TSTOP(kFLEXIO_TimerStopBitEnableOnTimerDisable);
372 }
373
374 /* configure data mode. */
375 if ((dataFormat == (uint8_t)kFLEXIO_SPI_8bitMsb) || (dataFormat == (uint8_t)kFLEXIO_SPI_8bitLsb))
376 {
377 dataMode = (8UL * 2UL - 1UL) << 8U;
378 }
379 else if ((dataFormat == (uint8_t)kFLEXIO_SPI_16bitMsb) || (dataFormat == (uint8_t)kFLEXIO_SPI_16bitLsb))
380 {
381 dataMode = (16UL * 2UL - 1UL) << 8U;
382 }
383 else if ((dataFormat == (uint8_t)kFLEXIO_SPI_32bitMsb) || (dataFormat == (uint8_t)kFLEXIO_SPI_32bitLsb))
384 {
385 dataMode = (32UL * 2UL - 1UL) << 8U;
386 }
387 else
388 {
389 dataMode = (8UL * 2UL - 1UL) << 8U;
390 }
391
392 dataMode |= timerCmp;
393
394 base->flexioBase->TIMCMP[base->timerIndex[0]] = dataMode;
395
396 return FLEXIO_SPI_DMAConfig(base, handle, xfer);
397 }
398
399 /*!
400 * brief Gets the remaining bytes for FlexIO SPI DMA transfer.
401 *
402 * param base Pointer to FLEXIO_SPI_Type structure.
403 * param handle FlexIO SPI DMA handle pointer.
404 * param count Number of bytes transferred so far by the non-blocking transaction.
405 */
FLEXIO_SPI_MasterTransferGetCountDMA(FLEXIO_SPI_Type * base,flexio_spi_master_dma_handle_t * handle,size_t * count)406 status_t FLEXIO_SPI_MasterTransferGetCountDMA(FLEXIO_SPI_Type *base,
407 flexio_spi_master_dma_handle_t *handle,
408 size_t *count)
409 {
410 assert(handle != NULL);
411
412 if (NULL == count)
413 {
414 return kStatus_InvalidArgument;
415 }
416
417 if (handle->rxInProgress)
418 {
419 *count = (handle->transferSize - DMA_GetRemainingBytes(handle->rxHandle->base, handle->rxHandle->channel));
420 }
421 else
422 {
423 *count = (handle->transferSize - DMA_GetRemainingBytes(handle->txHandle->base, handle->txHandle->channel));
424 }
425
426 return kStatus_Success;
427 }
428
429 /*!
430 * brief Aborts a FlexIO SPI transfer using DMA.
431 *
432 * param base Pointer to FLEXIO_SPI_Type structure.
433 * param handle FlexIO SPI DMA handle pointer.
434 */
FLEXIO_SPI_MasterTransferAbortDMA(FLEXIO_SPI_Type * base,flexio_spi_master_dma_handle_t * handle)435 void FLEXIO_SPI_MasterTransferAbortDMA(FLEXIO_SPI_Type *base, flexio_spi_master_dma_handle_t *handle)
436 {
437 assert(handle != NULL);
438
439 /* Disable dma. */
440 DMA_AbortTransfer(handle->txHandle);
441 DMA_AbortTransfer(handle->rxHandle);
442
443 /* Disable DMA enable bit. */
444 FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_DmaAllEnable, false);
445
446 /* Set the handle state. */
447 handle->txInProgress = false;
448 handle->rxInProgress = false;
449 }
450
451 /*!
452 * brief Performs a non-blocking FlexIO SPI transfer using DMA.
453 *
454 * note This interface returns immediately after transfer initiates. Call
455 * FLEXIO_SPI_SlaveGetTransferCountDMA to poll the transfer status and
456 * check whether the FlexIO SPI transfer is finished.
457 *
458 * param base Pointer to FLEXIO_SPI_Type structure.
459 * param handle Pointer to flexio_spi_slave_dma_handle_t structure to store the transfer state.
460 * param xfer Pointer to FlexIO SPI transfer structure.
461 * retval kStatus_Success Successfully start a transfer.
462 * retval kStatus_InvalidArgument Input argument is invalid.
463 * retval kStatus_FLEXIO_SPI_Busy FlexIO SPI is not idle, is running another transfer.
464 */
FLEXIO_SPI_SlaveTransferDMA(FLEXIO_SPI_Type * base,flexio_spi_slave_dma_handle_t * handle,flexio_spi_transfer_t * xfer)465 status_t FLEXIO_SPI_SlaveTransferDMA(FLEXIO_SPI_Type *base,
466 flexio_spi_slave_dma_handle_t *handle,
467 flexio_spi_transfer_t *xfer)
468 {
469 assert(handle != NULL);
470 assert(xfer != NULL);
471
472 uint32_t dataMode = 0U;
473 uint8_t dataFormat = FLEXIO_SPI_XFER_DATA_FORMAT(xfer->flags);
474
475 /* Check if the device is busy. */
476 if ((handle->txInProgress) || (handle->rxInProgress))
477 {
478 return kStatus_FLEXIO_SPI_Busy;
479 }
480
481 /* Check if input parameter invalid. */
482 if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U))
483 {
484 return kStatus_InvalidArgument;
485 }
486
487 /* SCK timer use CS pin as inverted trigger so timer should be disbaled on trigger falling edge(CS re-asserts). */
488 /* However if CPHA is first edge mode, timer will restart each time right after timer compare event occur and
489 before CS pin re-asserts, which triggers another shifter load. To avoid this, when in CS dis-continuous mode,
490 timer should disable in timer compare rather than trigger falling edge(CS re-asserts), and in CS continuous mode,
491 tx/rx shifters should be flushed after transfer finishes and before next transfer starts. */
492 FLEXIO_SPI_FlushShifters(base);
493 if ((xfer->flags & (uint8_t)kFLEXIO_SPI_csContinuous) != 0U)
494 {
495 base->flexioBase->TIMCFG[base->timerIndex[0]] |= FLEXIO_TIMCFG_TIMDIS(kFLEXIO_TimerDisableOnTriggerFallingEdge);
496 }
497 else
498 {
499 if ((base->flexioBase->SHIFTCTL[base->shifterIndex[0]] & FLEXIO_SHIFTCTL_TIMPOL_MASK) ==
500 FLEXIO_SHIFTCTL_TIMPOL(kFLEXIO_ShifterTimerPolarityOnNegitive))
501 {
502 base->flexioBase->TIMCFG[base->timerIndex[0]] =
503 (base->flexioBase->TIMCFG[base->timerIndex[0]] & ~FLEXIO_TIMCFG_TIMDIS_MASK) |
504 FLEXIO_TIMCFG_TIMDIS(kFLEXIO_TimerDisableOnTimerCompare);
505 }
506 else
507 {
508 base->flexioBase->TIMCFG[base->timerIndex[0]] =
509 (base->flexioBase->TIMCFG[base->timerIndex[0]] & ~FLEXIO_TIMCFG_TIMDIS_MASK) |
510 FLEXIO_TIMCFG_TIMDIS(kFLEXIO_TimerDisableOnTriggerFallingEdge);
511 }
512 }
513
514 /* configure data mode. */
515 if ((dataFormat == (uint8_t)kFLEXIO_SPI_8bitMsb) || (dataFormat == (uint8_t)kFLEXIO_SPI_8bitLsb))
516 {
517 dataMode = 8UL * 2UL - 1UL;
518 }
519 else if ((dataFormat == (uint8_t)kFLEXIO_SPI_16bitMsb) || (dataFormat == (uint8_t)kFLEXIO_SPI_16bitLsb))
520 {
521 dataMode = 16UL * 2UL - 1UL;
522 }
523 else if ((dataFormat == (uint8_t)kFLEXIO_SPI_32bitMsb) || (dataFormat == (uint8_t)kFLEXIO_SPI_32bitLsb))
524 {
525 dataMode = 32UL * 2UL - 1UL;
526 }
527 else
528 {
529 dataMode = 8UL * 2UL - 1UL;
530 }
531
532 base->flexioBase->TIMCMP[base->timerIndex[0]] = dataMode;
533
534 return FLEXIO_SPI_DMAConfig(base, handle, xfer);
535 }
536