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