1 /*
2  * Copyright 2016-2020 NXP
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include "fsl_ecspi_sdma.h"
8 
9 /*******************************************************************************
10  * Definitions
11  ******************************************************************************/
12 
13 /* Component ID definition, used by tools. */
14 #ifndef FSL_COMPONENT_ID
15 #define FSL_COMPONENT_ID "platform.drivers.ecspi_sdma"
16 #endif
17 
18 /*!
19  * @brief Structure definition for ecspi_master_sdma_private_handle_t. The structure is private.
20  */
21 typedef struct _ecspi_master_sdma_private_handle
22 {
23     ECSPI_Type *base;            /*!< ECSPI peripheral base address. */
24     ecspi_sdma_handle_t *handle; /*!< ecspi_sdma_handle_t handle */
25 } ecspi_master_sdma_private_handle_t;
26 
27 /*!
28  * @brief Structure definition for ecspi_slave_sdma_private_handle_t. The structure is private.
29  */
30 typedef struct _ecspi_slave_sdma_private_handle
31 {
32     ECSPI_Type *base;            /*!< ECSPI peripheral base address. */
33     ecspi_sdma_handle_t *handle; /*!< ecspi_sdma_handle_t handle */
34 } ecspi_slave_sdma_private_handle_t;
35 
36 /*! @brief ECSPI transfer state, which is used for ECSPI transactiaonl APIs' internal state. */
37 enum _ecspi_sdma_states_t
38 {
39     kECSPI_Idle = 0x0, /*!< ECSPI is idle state */
40     kECSPI_Busy        /*!< ECSPI is busy tranferring data. */
41 };
42 /*! @brief Base pointer array */
43 static ECSPI_Type *const s_ecspiBases[] = ECSPI_BASE_PTRS;
44 /*<! Private handle only used for internally. */
45 static ecspi_master_sdma_private_handle_t s_ecspiMasterSdmaPrivateHandle[ARRAY_SIZE(s_ecspiBases)];
46 static ecspi_slave_sdma_private_handle_t s_ecspiSlaveSdmaPrivateHandle[ARRAY_SIZE(s_ecspiBases)];
47 /*******************************************************************************
48  * Prototypes
49  ******************************************************************************/
50 /*!
51  * @brief SDMA_EcspiMasterCallback after the ECSPI master transfer completed by using SDMA.
52  * This is not a public API.
53  */
54 static void SDMA_EcspiMasterCallback(sdma_handle_t *sdmaHandle,
55                                      void *g_ecspiSdmaPrivateHandle,
56                                      bool transferDone,
57                                      uint32_t tcds);
58 
59 /*!
60  * @brief SDMA_EcspiSlaveCallback after the ECSPI slave transfer completed by using SDMA.
61  * This is not a public API.
62  */
63 static void SDMA_EcspiSlaveCallback(sdma_handle_t *sdmaHandle,
64                                     void *g_ecspiSdmaPrivateHandle,
65                                     bool transferDone,
66                                     uint32_t tcds);
67 /*******************************************************************************
68  * Variables
69  ******************************************************************************/
70 
71 /*******************************************************************************
72  * Code
73  ******************************************************************************/
SDMA_EcspiMasterCallback(sdma_handle_t * sdmaHandle,void * g_ecspiSdmaPrivateHandle,bool transferDone,uint32_t tcds)74 static void SDMA_EcspiMasterCallback(sdma_handle_t *sdmaHandle,
75                                      void *g_ecspiSdmaPrivateHandle,
76                                      bool transferDone,
77                                      uint32_t tcds)
78 {
79     assert(sdmaHandle != NULL);
80     assert(g_ecspiSdmaPrivateHandle != NULL);
81 
82     ecspi_master_sdma_private_handle_t *ecspiSdmaPrivateHandle;
83 
84     ecspiSdmaPrivateHandle = (ecspi_master_sdma_private_handle_t *)g_ecspiSdmaPrivateHandle;
85     /* if channel is Tx channel, disable Tx channel SDMA enable*/
86     if (sdmaHandle->channel == ecspiSdmaPrivateHandle->handle->ChannelTx)
87     {
88         ECSPI_EnableDMA((ecspiSdmaPrivateHandle->base), kECSPI_TxDmaEnable, false);
89         ecspiSdmaPrivateHandle->handle->txInProgress = false;
90         SDMA_SetChannelPriority(sdmaHandle->base, sdmaHandle->channel, 0);
91         SDMA_AbortTransfer(sdmaHandle);
92     }
93     /* if channel is Rx channel, disable Rx channel SDMA enable*/
94     else
95     {
96         ECSPI_EnableDMA((ecspiSdmaPrivateHandle->base), kECSPI_RxDmaEnable, false);
97         ecspiSdmaPrivateHandle->handle->rxInProgress = false;
98         SDMA_SetChannelPriority(sdmaHandle->base, sdmaHandle->channel, 0);
99         SDMA_AbortTransfer(sdmaHandle);
100     }
101     /* if both channel is finished, then abort SDMA transfer*/
102     if ((ecspiSdmaPrivateHandle->handle->txInProgress == false) &&
103         (ecspiSdmaPrivateHandle->handle->rxInProgress == false))
104     {
105         ECSPI_MasterTransferAbortSDMA(ecspiSdmaPrivateHandle->base, ecspiSdmaPrivateHandle->handle);
106         if (ecspiSdmaPrivateHandle->handle->callback != NULL)
107         {
108             ecspiSdmaPrivateHandle->handle->callback(ecspiSdmaPrivateHandle->base, ecspiSdmaPrivateHandle->handle,
109                                                      kStatus_Success, ecspiSdmaPrivateHandle->handle->userData);
110         }
111         ecspiSdmaPrivateHandle->handle->state = (uint32_t)kECSPI_Idle;
112     }
113 }
114 
115 /*!
116  * brief Abort a ECSPI master transfer using SDMA.
117  *
118  * param base ECSPI peripheral base address.
119  * param handle ECSPI SDMA handle pointer.
120  */
ECSPI_MasterTransferAbortSDMA(ECSPI_Type * base,ecspi_sdma_handle_t * handle)121 void ECSPI_MasterTransferAbortSDMA(ECSPI_Type *base, ecspi_sdma_handle_t *handle)
122 {
123     assert(handle != NULL);
124 
125     ECSPI_Enable(base, false);
126 
127     ECSPI_EnableDMA(base, kECSPI_DmaAllEnable, false);
128 
129     SDMA_AbortTransfer(handle->rxSdmaHandle);
130     SDMA_AbortTransfer(handle->txSdmaHandle);
131 
132     handle->state = (uint32_t)kECSPI_Idle;
133 }
134 
SDMA_EcspiSlaveCallback(sdma_handle_t * sdmaHandle,void * g_ecspiSdmaPrivateHandle,bool transferDone,uint32_t tcds)135 static void SDMA_EcspiSlaveCallback(sdma_handle_t *sdmaHandle,
136                                     void *g_ecspiSdmaPrivateHandle,
137                                     bool transferDone,
138                                     uint32_t tcds)
139 {
140     assert(sdmaHandle != NULL);
141     assert(g_ecspiSdmaPrivateHandle != NULL);
142 
143     ecspi_slave_sdma_private_handle_t *ecspiSdmaPrivateHandle;
144 
145     ecspiSdmaPrivateHandle = (ecspi_slave_sdma_private_handle_t *)g_ecspiSdmaPrivateHandle;
146     /* if channel is Tx channel, disable Tx channel SDMA enable*/
147     if (sdmaHandle->channel == ecspiSdmaPrivateHandle->handle->ChannelTx)
148     {
149         ECSPI_EnableDMA((ecspiSdmaPrivateHandle->base), kECSPI_TxDmaEnable, false);
150         ecspiSdmaPrivateHandle->handle->txInProgress = false;
151         SDMA_SetChannelPriority(sdmaHandle->base, sdmaHandle->channel, 0);
152         SDMA_AbortTransfer(sdmaHandle);
153     }
154     /* if channel is Rx channel, disable Rx channel SDMA enable*/
155     else
156     {
157         ECSPI_EnableDMA((ecspiSdmaPrivateHandle->base), kECSPI_RxDmaEnable, false);
158         ecspiSdmaPrivateHandle->handle->rxInProgress = false;
159         SDMA_SetChannelPriority(sdmaHandle->base, sdmaHandle->channel, 0);
160         SDMA_AbortTransfer(sdmaHandle);
161     }
162     /* if both channel is finished, then abort SDMA transfer*/
163     if ((ecspiSdmaPrivateHandle->handle->txInProgress == false) &&
164         (ecspiSdmaPrivateHandle->handle->rxInProgress == false))
165     {
166         ECSPI_MasterTransferAbortSDMA(ecspiSdmaPrivateHandle->base, ecspiSdmaPrivateHandle->handle);
167         if (ecspiSdmaPrivateHandle->handle->callback != NULL)
168         {
169             ecspiSdmaPrivateHandle->handle->callback(ecspiSdmaPrivateHandle->base, ecspiSdmaPrivateHandle->handle,
170                                                      kStatus_Success, ecspiSdmaPrivateHandle->handle->userData);
171         }
172         ecspiSdmaPrivateHandle->handle->state = (uint32_t)kECSPI_Idle;
173     }
174 }
175 
176 /*!
177  * brief Abort a ECSPI slave transfer using SDMA.
178  *
179  * param base ECSPI peripheral base address.
180  * param handle ECSPI SDMA handle pointer.
181  */
ECSPI_SlaveTransferAbortSDMA(ECSPI_Type * base,ecspi_sdma_handle_t * handle)182 void ECSPI_SlaveTransferAbortSDMA(ECSPI_Type *base, ecspi_sdma_handle_t *handle)
183 {
184     assert(handle != NULL);
185 
186     ECSPI_Enable(base, false);
187 
188     ECSPI_EnableDMA(base, kECSPI_RxDmaEnable, false);
189     ECSPI_EnableDMA(base, kECSPI_TxDmaEnable, false);
190 
191     SDMA_AbortTransfer(handle->rxSdmaHandle);
192     SDMA_AbortTransfer(handle->txSdmaHandle);
193 
194     handle->state = (uint32_t)kECSPI_Idle;
195 }
196 
197 /*!
198  * brief Initialize the ECSPI master SDMA handle.
199  *
200  * This function initializes the ECSPI master SDMA handle which can be used for other SPI master transactional APIs.
201  * Usually, for a specified ECSPI instance, user need only call this API once to get the initialized handle.
202  *
203  * param base ECSPI peripheral base address.
204  * param handle ECSPI handle pointer.
205  * param callback User callback function called at the end of a transfer.
206  * param userData User data for callback.
207  * param txHandle SDMA handle pointer for ECSPI Tx, the handle shall be static allocated by users.
208  * param rxHandle SDMA handle pointer for ECSPI Rx, the handle shall be static allocated by users.
209  * param eventSourceTx event source for ECSPI send, which can be found in SDMA mapping.
210  * param eventSourceRx event source for ECSPI receive, which can be found in SDMA mapping.
211  * param TxChannel SDMA channel for ECSPI send.
212  * param RxChannel SDMA channel for ECSPI receive.
213  */
ECSPI_MasterTransferCreateHandleSDMA(ECSPI_Type * base,ecspi_sdma_handle_t * handle,ecspi_sdma_callback_t callback,void * userData,sdma_handle_t * txHandle,sdma_handle_t * rxHandle,uint32_t eventSourceTx,uint32_t eventSourceRx,uint32_t TxChannel,uint32_t RxChannel)214 void ECSPI_MasterTransferCreateHandleSDMA(ECSPI_Type *base,
215                                           ecspi_sdma_handle_t *handle,
216                                           ecspi_sdma_callback_t callback,
217                                           void *userData,
218                                           sdma_handle_t *txHandle,
219                                           sdma_handle_t *rxHandle,
220                                           uint32_t eventSourceTx,
221                                           uint32_t eventSourceRx,
222                                           uint32_t TxChannel,
223                                           uint32_t RxChannel)
224 {
225     assert(handle != NULL);
226     assert(txHandle != NULL);
227     assert(rxHandle != NULL);
228     uint32_t instance = ECSPI_GetInstance(base);
229 
230     /* Zero the handle */
231     (void)memset(handle, 0, sizeof(*handle));
232 
233     /* Set ECSPI base to handle */
234     rxHandle->eventSource = eventSourceRx;
235     txHandle->eventSource = eventSourceTx;
236     handle->txSdmaHandle  = txHandle;
237     handle->rxSdmaHandle  = rxHandle;
238     handle->ChannelTx     = TxChannel;
239     handle->ChannelRx     = RxChannel;
240     handle->callback      = callback;
241     handle->userData      = userData;
242 
243     /* Set ECSPI state to idle */
244     handle->state = (uint32_t)kECSPI_Idle;
245 
246     /* Set handle to global state */
247     s_ecspiMasterSdmaPrivateHandle[instance].base   = base;
248     s_ecspiMasterSdmaPrivateHandle[instance].handle = handle;
249 
250     /* Install callback for Tx and Rx sdma channel */
251     SDMA_SetCallback(handle->rxSdmaHandle, SDMA_EcspiMasterCallback, &s_ecspiMasterSdmaPrivateHandle[instance]);
252     SDMA_SetCallback(handle->txSdmaHandle, SDMA_EcspiMasterCallback, &s_ecspiMasterSdmaPrivateHandle[instance]);
253 }
254 
255 /*!
256  * brief Initialize the ECSPI Slave SDMA handle.
257  *
258  * This function initializes the ECSPI Slave SDMA handle which can be used for other SPI Slave transactional APIs.
259  * Usually, for a specified ECSPI instance, user need only call this API once to get the initialized handle.
260  *
261  * param base ECSPI peripheral base address.
262  * param handle ECSPI handle pointer.
263  * param callback User callback function called at the end of a transfer.
264  * param userData User data for callback.
265  * param txHandle SDMA handle pointer for ECSPI Tx, the handle shall be static allocated by users.
266  * param rxHandle SDMA handle pointer for ECSPI Rx, the handle shall be static allocated by users.
267  * param eventSourceTx event source for ECSPI send, which can be found in SDMA mapping.
268  * param eventSourceRx event source for ECSPI receive, which can be found in SDMA mapping.
269  * param TxChannel SDMA channel for ECSPI send.
270  * param RxChannel SDMA channel for ECSPI receive.
271  */
ECSPI_SlaveTransferCreateHandleSDMA(ECSPI_Type * base,ecspi_sdma_handle_t * handle,ecspi_sdma_callback_t callback,void * userData,sdma_handle_t * txHandle,sdma_handle_t * rxHandle,uint32_t eventSourceTx,uint32_t eventSourceRx,uint32_t TxChannel,uint32_t RxChannel)272 void ECSPI_SlaveTransferCreateHandleSDMA(ECSPI_Type *base,
273                                          ecspi_sdma_handle_t *handle,
274                                          ecspi_sdma_callback_t callback,
275                                          void *userData,
276                                          sdma_handle_t *txHandle,
277                                          sdma_handle_t *rxHandle,
278                                          uint32_t eventSourceTx,
279                                          uint32_t eventSourceRx,
280                                          uint32_t TxChannel,
281                                          uint32_t RxChannel)
282 {
283     assert(handle != NULL);
284     assert(txHandle != NULL);
285     assert(rxHandle != NULL);
286     uint32_t instance = ECSPI_GetInstance(base);
287 
288     /* Zero the handle */
289     (void)memset(handle, 0, sizeof(*handle));
290 
291     /* Set ECSPI base to handle */
292     rxHandle->eventSource = eventSourceRx;
293     txHandle->eventSource = eventSourceTx;
294     handle->txSdmaHandle  = txHandle;
295     handle->rxSdmaHandle  = rxHandle;
296     handle->ChannelTx     = TxChannel;
297     handle->ChannelRx     = RxChannel;
298     handle->callback      = callback;
299     handle->userData      = userData;
300 
301     /* Set ECSPI state to idle */
302     handle->state = (uint32_t)kECSPI_Idle;
303 
304     /* Set handle to global state */
305     s_ecspiSlaveSdmaPrivateHandle[instance].base   = base;
306     s_ecspiSlaveSdmaPrivateHandle[instance].handle = handle;
307 
308     /* Install callback for Tx and Rx sdma channel */
309     SDMA_SetCallback(handle->txSdmaHandle, SDMA_EcspiSlaveCallback, &s_ecspiSlaveSdmaPrivateHandle[instance]);
310     SDMA_SetCallback(handle->rxSdmaHandle, SDMA_EcspiSlaveCallback, &s_ecspiSlaveSdmaPrivateHandle[instance]);
311 }
312 
313 /*!
314  * brief Perform a non-blocking ECSPI master transfer using SDMA.
315  *
316  * note This interface returned immediately after transfer initiates.
317  *
318  * param base ECSPI peripheral base address.
319  * param handle ECSPI SDMA handle pointer.
320  * param xfer Pointer to sdma transfer structure.
321  * retval kStatus_Success Successfully start a transfer.
322  * retval kStatus_InvalidArgument Input argument is invalid.
323  * retval kStatus_ECSPI_Busy EECSPI is not idle, is running another transfer.
324  */
ECSPI_MasterTransferSDMA(ECSPI_Type * base,ecspi_sdma_handle_t * handle,ecspi_transfer_t * xfer)325 status_t ECSPI_MasterTransferSDMA(ECSPI_Type *base, ecspi_sdma_handle_t *handle, ecspi_transfer_t *xfer)
326 {
327     assert((base != NULL) && (handle != NULL) && (xfer != NULL));
328 
329     sdma_transfer_config_t xferConfig = {0U};
330     sdma_peripheral_t perType         = kSDMA_PeripheralNormal;
331 
332     /* Check if ECSPI is busy */
333     if (handle->state == (uint32_t)kECSPI_Busy)
334     {
335         return kStatus_ECSPI_Busy;
336     }
337 
338     /* Check if the input arguments valid */
339     if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U))
340     {
341         return kStatus_InvalidArgument;
342     }
343     ECSPI_Enable(base, true);
344     handle->state = (uint32_t)kECSPI_Busy;
345 
346     ECSPI_SetChannelSelect(base, xfer->channel);
347 
348 #if defined(FSL_FEATURE_SOC_SPBA_COUNT) && (FSL_FEATURE_SOC_SPBA_COUNT > 0)
349     bool isSpba = SDMA_IsPeripheralInSPBA((uint32_t)base);
350     /* Judge if the instance is located in SPBA */
351     if (isSpba)
352     {
353         perType = kSDMA_PeripheralNormal_SP;
354     }
355 #endif /* FSL_FEATURE_SOC_SPBA_COUNT */
356 
357     /* Prepare transfer. */
358     SDMA_PrepareTransfer(&xferConfig, (uint32_t)xfer->txData, (uint32_t) & (base->TXDATA), sizeof(uint8_t),
359                          sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, handle->txSdmaHandle->eventSource, perType,
360                          kSDMA_MemoryToPeripheral);
361 
362     /* Submit transfer. */
363     SDMA_SubmitTransfer(handle->txSdmaHandle, &xferConfig);
364 
365     /* Prepare transfer. */
366     SDMA_PrepareTransfer(&xferConfig, (uint32_t) & (base->RXDATA), (uint32_t)xfer->rxData, sizeof(uint8_t),
367                          sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, handle->rxSdmaHandle->eventSource, perType,
368                          kSDMA_PeripheralToMemory);
369     /* Submit transfer. */
370     SDMA_SubmitTransfer(handle->rxSdmaHandle, &xferConfig);
371     /* Start Rx transfer */
372     handle->rxInProgress = true;
373     SDMA_StartTransfer(handle->rxSdmaHandle);
374     ECSPI_EnableDMA(base, kECSPI_RxDmaEnable, true);
375 
376     /* Start Tx transfer */
377     handle->txInProgress = true;
378     SDMA_StartTransfer(handle->txSdmaHandle);
379     ECSPI_EnableDMA(base, kECSPI_TxDmaEnable, true);
380 
381     return kStatus_Success;
382 }
383 
384 /*!
385  * brief Perform a non-blocking ECSPI slave transfer using SDMA.
386  *
387  * note This interface returned immediately after transfer initiates.
388  *
389  * param base ECSPI peripheral base address.
390  * param handle ECSPI SDMA handle pointer.
391  * param xfer Pointer to sdma transfer structure.
392  * retval kStatus_Success Successfully start a transfer.
393  * retval kStatus_InvalidArgument Input argument is invalid.
394  * retval kStatus_ECSPI_Busy EECSPI is not idle, is running another transfer.
395  */
ECSPI_SlaveTransferSDMA(ECSPI_Type * base,ecspi_sdma_handle_t * handle,ecspi_transfer_t * xfer)396 status_t ECSPI_SlaveTransferSDMA(ECSPI_Type *base, ecspi_sdma_handle_t *handle, ecspi_transfer_t *xfer)
397 {
398     assert((base != NULL) && (handle != NULL) && (xfer != NULL));
399 
400     sdma_transfer_config_t xferConfig;
401     sdma_peripheral_t perType = kSDMA_PeripheralNormal;
402 
403     /* Check if ECSPI is busy */
404     if (handle->state == (uint32_t)kECSPI_Busy)
405     {
406         return kStatus_ECSPI_Busy;
407     }
408 
409     /* Check if the input arguments valid */
410     if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U))
411     {
412         return kStatus_InvalidArgument;
413     }
414     ECSPI_Enable(base, true);
415     handle->state = (uint32_t)kECSPI_Busy;
416 
417     ECSPI_SetChannelSelect(base, xfer->channel);
418 
419 #if defined(FSL_FEATURE_SOC_SPBA_COUNT) && (FSL_FEATURE_SOC_SPBA_COUNT > 0)
420     bool isSpba = SDMA_IsPeripheralInSPBA((uint32_t)base);
421     /* Judge if the instance is located in SPBA */
422     if (isSpba)
423     {
424         perType = kSDMA_PeripheralNormal_SP;
425     }
426 #endif /* FSL_FEATURE_SOC_SPBA_COUNT */
427 
428     /* Prepare transfer. */
429     SDMA_PrepareTransfer(&xferConfig, (uint32_t) & (base->RXDATA), (uint32_t)xfer->rxData, sizeof(uint8_t),
430                          sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, handle->rxSdmaHandle->eventSource, perType,
431                          kSDMA_PeripheralToMemory);
432     /* Submit transfer. */
433     SDMA_SubmitTransfer(handle->rxSdmaHandle, &xferConfig);
434 
435     /* Prepare transfer. */
436     SDMA_PrepareTransfer(&xferConfig, (uint32_t)xfer->txData, (uint32_t) & (base->TXDATA), sizeof(uint8_t),
437                          sizeof(uint8_t), sizeof(uint8_t), xfer->dataSize, handle->txSdmaHandle->eventSource, perType,
438                          kSDMA_MemoryToPeripheral);
439 
440     /* Submit transfer. */
441     SDMA_SubmitTransfer(handle->txSdmaHandle, &xferConfig);
442     /* start Tx transfer */
443     handle->txInProgress = true;
444     ECSPI_EnableDMA(base, kECSPI_TxDmaEnable, true);
445     SDMA_StartTransfer(handle->txSdmaHandle);
446 
447     /* start Rx transfer */
448     handle->rxInProgress = true;
449     ECSPI_EnableDMA(base, kECSPI_RxDmaEnable, true);
450     SDMA_StartTransfer(handle->rxSdmaHandle);
451 
452     return kStatus_Success;
453 }
454