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