1 /*
2  * Copyright 2021-2023 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_gdma.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.gdma"
17 #endif
18 
19 /* Is address aligned? */
20 #define GDMA_IS_ADDR_ALIGNED(addr, aligned_size) (((uint32_t)(addr) & ((aligned_size)-1UL)) == 0U)
21 /* Is the wrap used? */
22 #define GDMA_IS_WRAP_BURST(burstsize) (((uint8_t)(burstsize)&0x04U) != 0U)
23 /* Get real width from enum gdma_transfer_width_t. */
24 #define GDMA_REAL_XFER_WIDTH(width) (s_gdmaRealWidth[(uint8_t)(width)&0x03U])
25 /* Get real burst size from gdma_burst_size_t. */
26 #define GDMA_REAL_XFER_BUSTSIZE(burstsize) (s_gdmaRealBurstSize[(uint8_t)(burstsize)&0x03U])
27 
28 /*******************************************************************************
29  * Prototypes
30  ******************************************************************************/
31 /* Typedef for interrupt handler. */
32 typedef void (*gdma_isr_t)(GDMA_Type *base);
33 
34 /*!
35  * @brief Verify the configuration.
36  *
37  * Verify the configuration, to make sure the parameters are valid.
38  *
39  * @param config Pointer to the transfer configuration.
40  * @return Return true if the configuration is valid, otherwise return false.
41  */
42 static bool GDMA_VerifyTransferConfig(const gdma_channel_xfer_config_t *config);
43 
44 /*******************************************************************************
45  * Variables
46  ******************************************************************************/
47 
48 /* @brief Pointers to transfer handle for each GDMA channel. */
49 static gdma_handle_t *s_gdmaHandles[FSL_FEATURE_GDMA_CHANNEL_NUM];
50 
51 static const uint8_t s_gdmaRealBurstSize[] = {1U, 4U, 8U, 16U};
52 static const uint8_t s_gdmaRealWidth[]     = {1U, 1U, 2U, 4U};
53 
54 /* ISR for transactional APIs. */
55 #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
56 static gdma_isr_t s_gdmaIsr = (gdma_isr_t)DefaultISR;
57 #else
58 static gdma_isr_t s_gdmaIsr;
59 #endif
60 
61 /*******************************************************************************
62  * Code
63  ******************************************************************************/
64 
65 /*
66  * brief Verify the configuration.
67  *
68  * Verify the configuration, to make sure the parameters are valid.
69  *
70  * param config Pointer to the transfer configuration.
71  * return Return true if the configuration is valid, otherwise return false.
72  */
GDMA_VerifyTransferConfig(const gdma_channel_xfer_config_t * config)73 static bool GDMA_VerifyTransferConfig(const gdma_channel_xfer_config_t *config)
74 {
75     bool ret              = false;
76     uint8_t srcWidth      = GDMA_REAL_XFER_WIDTH(config->srcWidth);
77     uint8_t destWidth     = GDMA_REAL_XFER_WIDTH(config->destWidth);
78     uint8_t srcBurstSize  = GDMA_REAL_XFER_BUSTSIZE(config->srcBurstSize);
79     uint8_t destBurstSize = GDMA_REAL_XFER_BUSTSIZE(config->destBurstSize);
80     uint8_t srcAddrAlignSize;
81     uint8_t destAddrAlignSize;
82 
83     do
84     {
85         /*  SRCBSIZE * SRCWIDTH == DESTBSIZE * DESTWIDTH */
86         if ((srcWidth * srcBurstSize) != (destWidth * destBurstSize))
87         {
88             break;
89         }
90 
91         /*
92          * Address alignment:
93          *
94          * From GDMA's view, the address only need to be aligned with the WIDTH (no matter wrap used or not).
95          * When integrating with AHB and wrap is used, the address should be aligned to WIDTH * BURST_SIZE.
96          */
97         srcAddrAlignSize  = (GDMA_IS_WRAP_BURST(config->srcBurstSize)) ? (srcWidth * srcBurstSize) : srcWidth;
98         destAddrAlignSize = (GDMA_IS_WRAP_BURST(config->destBurstSize)) ? (destWidth * destBurstSize) : destWidth;
99 
100         if (!((GDMA_IS_ADDR_ALIGNED(config->srcAddr, srcAddrAlignSize)) &&
101               (GDMA_IS_ADDR_ALIGNED(config->destAddr, destAddrAlignSize))))
102         {
103             break;
104         }
105 
106         ret = true;
107 
108     } while (false);
109 
110     return ret;
111 }
112 
113 /*
114  * brief Initializes GDMA peripheral.
115  *
116  * This function enable the GDMA clock, set descriptor table and
117  * enable GDMA peripheral.
118  *
119  * param base GDMA peripheral base address.
120  */
GDMA_Init(GDMA_Type * base)121 void GDMA_Init(GDMA_Type *base)
122 {
123 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
124     CLOCK_EnableClock(kCLOCK_Gdma);
125 #endif
126 }
127 
128 /*
129  * brief Deinitializes GDMA peripheral.
130  *
131  * This function gates the GDMA clock.
132  *
133  * param base GDMA peripheral base address.
134  */
GDMA_Deinit(GDMA_Type * base)135 void GDMA_Deinit(GDMA_Type *base)
136 {
137 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
138     CLOCK_DisableClock(kCLOCK_Gdma);
139 #endif
140 }
141 
142 /*
143  * brief Set channel transfer configuration..
144  *
145  * This function configures the channel transfer, after configured, GDMA_StartChannel
146  * could be called to start the transfer.
147  *
148  * This function must be called when previous transfer finished. Application can use
149  * GDMA_IsChannelBusy to check whether the channel has finished the previous work.
150  *
151  * param base GDMA base address.
152  * param channel GDMA channel number.
153  * config Pointer to the transfer configuration.
154  * retval kStatus_Fail GDMA is busy with previous transfer.
155  * retval kStatus_Success Configuration set successfully.
156  * retval kStatus_InvalidArgument Configuration wrong.
157  */
GDMA_SetChannelTransferConfig(GDMA_Type * base,uint8_t channel,const gdma_channel_xfer_config_t * config)158 status_t GDMA_SetChannelTransferConfig(GDMA_Type *base, uint8_t channel, const gdma_channel_xfer_config_t *config)
159 {
160     assert(NULL != config);
161 
162     if (!GDMA_VerifyTransferConfig(config))
163     {
164         return kStatus_InvalidArgument;
165     }
166 
167     if (GDMA_IsChannelBusy(base, channel))
168     {
169         return kStatus_Fail;
170     }
171 
172     GDMA_ClearChannelInterruptFlags(base, channel, (uint32_t)kGDMA_AllInterruptFlag);
173 
174     base->CH[channel].SADR = config->srcAddr;
175     base->CH[channel].DADR = config->destAddr;
176 
177     base->CH[channel].CTRL =
178         GDMA_CTRL_PROT(config->ahbProt)                            /* Protection info for AHB master bus */
179         | (config->srcAddrInc ? GDMA_CTRL_SRCADDRINC_MASK : 0UL)   /* Source address increment. */
180         | (config->destAddrInc ? GDMA_CTRL_DESTADDRINC_MASK : 0UL) /* Destination address increment. */
181         | GDMA_CTRL_SRCWIDTH(config->srcWidth)                     /* Source peripheral/memory transfer width. */
182         | GDMA_CTRL_DESTWIDTH(config->destWidth)                   /* Destination peripheral/memory transfer width. */
183         | GDMA_CTRL_SRCBSIZE(config->srcBurstSize)                 /* Source peripheral/memory transfer burst size */
184         | GDMA_CTRL_DESTBSIZE(config->destBurstSize) /* Destination peripheral/memory transfer burst size */
185         | GDMA_CTRL_LEN(config->transferLen);        /* Length of the transfer in bytes */
186 
187     if (config->enableLinkList)
188     {
189         base->CH[channel].LLI =
190             (config->linkListAddr & GDMA_LLI_LLI_MASK)                        /* LLI address. */
191             | (config->enableDescInterrupt ? GDMA_LLI_DESC_INT_EN_MASK : 0UL) /* Enable descriptor interrupt. */
192             | (config->stopAfterDescFinished ? GDMA_LLI_STOP_MASK : 0UL);     /* Stop after descriptor finished. */
193         base->CH[channel].CONFIG |= GDMA_CONFIG_LLE_MASK;
194     }
195     else
196     {
197         base->CH[channel].LLI = GDMA_LLI_STOP_MASK;
198         base->CH[channel].CONFIG &= ~GDMA_CONFIG_LLE_MASK;
199     }
200 
201     return kStatus_Success;
202 }
203 
204 /*
205  * brief Creates the GDMA handle.
206  *
207  * This function is called if using transaction API for GDMA. This function
208  * initializes the internal state of GDMA handle.
209  *
210  * param handle GDMA handle pointer. It stores callback function and parameters.
211  * param base GDMA peripheral base address.
212  * param channel GDMA channel number.
213  */
GDMA_CreateHandle(gdma_handle_t * handle,GDMA_Type * base,uint8_t channel)214 void GDMA_CreateHandle(gdma_handle_t *handle, GDMA_Type *base, uint8_t channel)
215 {
216     assert(NULL != handle);
217 
218     (void)memset(handle, 0, sizeof(*handle));
219 
220     handle->gdma    = base;
221     handle->channel = channel;
222 
223     s_gdmaHandles[channel] = handle;
224     s_gdmaIsr              = GDMA_IRQHandle;
225 
226     (void)EnableIRQ(GDMA_IRQn);
227 }
228 
229 /*
230  * brief Installs a callback function for the GDMA transfer.
231  *
232  * This callback is called in GDMA IRQ handler to inform user the interrupt status.
233  *
234  * param handle GDMA handle pointer.
235  * param callback GDMA callback function pointer.
236  * param userData Parameter for callback function.
237  */
GDMA_SetCallback(gdma_handle_t * handle,gdma_callback_t callback,void * userData)238 void GDMA_SetCallback(gdma_handle_t *handle, gdma_callback_t callback, void *userData)
239 {
240     assert(handle != NULL);
241 
242     handle->callback = callback;
243     handle->userData = userData;
244 }
245 
246 /*
247  * brief Submits the GDMA channel transfer request.
248  *
249  * After this function, user could call GDMA_StartTransfer to start GDMA transfer.
250  *
251  * This function must be called when previous transfer finished. Application can use
252  * GDMA_IsChannelBusy to check whether the channel has finished the previous work.
253  *
254  * param handle GDMA handle pointer.
255  * param config Pointer to GDMA transfer configuration structure.
256  * retval kStatus_Fail GDMA is busy with previous transfer.
257  * retval kStatus_Success Configuration set successfully.
258  * retval kStatus_InvalidArgument Configuration wrong.
259  */
GDMA_SubmitTransfer(gdma_handle_t * handle,gdma_channel_xfer_config_t * config)260 status_t GDMA_SubmitTransfer(gdma_handle_t *handle, gdma_channel_xfer_config_t *config)
261 {
262     status_t status;
263 
264     assert(NULL != handle);
265 
266     status = GDMA_SetChannelTransferConfig(handle->gdma, handle->channel, config);
267 
268     if (status == kStatus_Success)
269     {
270         GDMA_EnableChannelInterrupts(
271             handle->gdma, handle->channel,
272             (uint32_t)kGDMA_DescriptorTransferDoneInterruptEnable | (uint32_t)kGDMA_AddressErrorInterruptEnable |
273                 (uint32_t)kGDMA_BusErrorInterruptEnable | (uint32_t)kGDMA_TransferDoneInterruptEnable);
274     }
275 
276     return status;
277 }
278 
279 /*
280  * brief GDMA start transfer.
281  *
282  * User can call this function after GDMA_SubmitTransfer.
283  *
284  * param handle GDMA handle pointer.
285  */
GDMA_StartTransfer(gdma_handle_t * handle)286 void GDMA_StartTransfer(gdma_handle_t *handle)
287 {
288     assert(NULL != handle);
289 
290     GDMA_StartChannel(handle->gdma, handle->channel);
291 }
292 
293 /*
294  * brief Abort running transfer by handle.
295  *
296  * This function aborts GDMA transfer specified by handle.
297  *
298  * param handle GDMA handle pointer.
299  */
GDMA_AbortTransfer(gdma_handle_t * handle)300 void GDMA_AbortTransfer(gdma_handle_t *handle)
301 {
302     assert(NULL != handle);
303 
304     GDMA_StopChannel(handle->gdma, handle->channel);
305 
306     GDMA_DisableChannelInterrupts(handle->gdma, handle->channel, (uint32_t)kGDMA_AllInterruptEnable);
307 }
308 
309 /*
310  * brief GDMA IRQ handler.
311  *
312  * This function checks all GDMA channel interrupts and inform application
313  * the interrupt flags through user registered callback.
314  *
315  * param base GDMA peripheral.
316  */
GDMA_IRQHandle(GDMA_Type * base)317 void GDMA_IRQHandle(GDMA_Type *base)
318 {
319     uint8_t channel;
320     uint32_t interrupts;
321     gdma_handle_t *handle;
322 
323     for (channel = 0U; channel < (uint8_t)FSL_FEATURE_GDMA_CHANNEL_NUM; channel++)
324     {
325         interrupts = GDMA_GetChannelInterruptFlags(base, channel);
326 
327         /*
328          * If channels unmasked interrupt happened. This flag is only assert
329          * only when any unmasked interrupt happened.
330          */
331         if (0U != (interrupts & (uint32_t)kGDMA_ChannelInterruptFlag))
332         {
333             GDMA_ClearChannelInterruptFlags(base, channel, interrupts);
334 
335             handle = s_gdmaHandles[channel];
336 
337             if (NULL != handle)
338             {
339                 /* If error happened or transfer finished successfully. */
340                 if (0U != (interrupts & ((uint32_t)kGDMA_AddressErrorFlag | (uint32_t)kGDMA_BusErrorFlag |
341                                          (uint32_t)kGDMA_TransferDoneFlag)))
342                 {
343                     GDMA_DisableChannelInterrupts(base, channel, (uint32_t)kGDMA_AllInterruptEnable);
344                 }
345 
346                 if (NULL != handle->callback)
347                 {
348                     handle->callback(handle, handle->userData, interrupts);
349                 }
350             }
351             else
352             {
353                 /*
354                  * If interrupt occurs but handle not created, then disable the interrupts,
355                  * because to use the transactional APIs, user must create handle, then
356                  * setup configuration and enable interrupts for data transfer.
357                  *
358                  * Generally will not reach here.
359                  */
360                 GDMA_DisableChannelInterrupts(base, channel, (uint32_t)kGDMA_AllInterruptEnable);
361             }
362         }
363     }
364 }
365 
366 void GDMA_DriverIRQHandler(void);
GDMA_DriverIRQHandler(void)367 void GDMA_DriverIRQHandler(void)
368 {
369     s_gdmaIsr(GDMA);
370     SDK_ISR_EXIT_BARRIER;
371 }
372