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 | |
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 | |
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 | |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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_AbortTransfer(handle->txHandle); |
410 | EDMA_AbortTransfer(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 | */ |
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 | |