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_dma.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.dma"
18 #endif
19
20 /*******************************************************************************
21 * Prototypes
22 ******************************************************************************/
23
24 /*!
25 * @brief Get instance number for DMA.
26 *
27 * @param base DMA peripheral base address.
28 */
29 static uint32_t DMA_GetInstance(DMA_Type *base);
30
31 /*******************************************************************************
32 * Variables
33 ******************************************************************************/
34
35 /*! @brief Array to map DMA instance number to base pointer. */
36 static DMA_Type *const s_dmaBases[] = DMA_BASE_PTRS;
37
38 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
39 /*! @brief Array to map DMA instance number to clock name. */
40 static const clock_ip_name_t s_dmaClockName[] = DMA_CLOCKS;
41 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
42
43 /*! @brief Array to map DMA instance number to IRQ number. */
44 static const IRQn_Type s_dmaIRQNumber[][FSL_FEATURE_DMA_MODULE_CHANNEL] = DMA_CHN_IRQS;
45
46 /*! @brief Pointers to transfer handle for each DMA channel. */
47 static dma_handle_t *s_DMAHandle[FSL_FEATURE_DMA_MODULE_CHANNEL * FSL_FEATURE_SOC_DMA_COUNT];
48
49 /*******************************************************************************
50 * Code
51 ******************************************************************************/
DMA_GetInstance(DMA_Type * base)52 static uint32_t DMA_GetInstance(DMA_Type *base)
53 {
54 uint32_t instance;
55
56 /* Find the instance index from base address mappings. */
57 for (instance = 0; instance < ARRAY_SIZE(s_dmaBases); instance++)
58 {
59 if (s_dmaBases[instance] == base)
60 {
61 break;
62 }
63 }
64
65 assert(instance < ARRAY_SIZE(s_dmaBases));
66
67 return instance;
68 }
69
70 /*!
71 * brief Initializes the DMA peripheral.
72 *
73 * This function ungates the DMA clock.
74 *
75 * param base DMA peripheral base address.
76 */
DMA_Init(DMA_Type * base)77 void DMA_Init(DMA_Type *base)
78 {
79 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
80 CLOCK_EnableClock(s_dmaClockName[DMA_GetInstance(base)]);
81 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
82 }
83
84 /*!
85 * brief Deinitializes the DMA peripheral.
86 *
87 * This function gates the DMA clock.
88 *
89 * param base DMA peripheral base address.
90 */
DMA_Deinit(DMA_Type * base)91 void DMA_Deinit(DMA_Type *base)
92 {
93 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
94 CLOCK_DisableClock(s_dmaClockName[DMA_GetInstance(base)]);
95 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
96 }
97
98 /*!
99 * brief Resets the DMA channel.
100 *
101 * Sets all register values to reset values and enables
102 * the cycle steal and auto stop channel request features.
103 *
104 * param base DMA peripheral base address.
105 * param channel DMA channel number.
106 */
DMA_ResetChannel(DMA_Type * base,uint32_t channel)107 void DMA_ResetChannel(DMA_Type *base, uint32_t channel)
108 {
109 assert(channel < (uint32_t)FSL_FEATURE_DMA_MODULE_CHANNEL);
110
111 /* clear all status bit */
112 base->DMA[channel].DSR_BCR |= DMA_DSR_BCR_DONE(true);
113 /* clear all registers */
114 base->DMA[channel].SAR = 0;
115 base->DMA[channel].DAR = 0;
116 base->DMA[channel].DSR_BCR = 0;
117 /* enable cycle steal and enable auto disable channel request */
118 base->DMA[channel].DCR = DMA_DCR_D_REQ(true) | DMA_DCR_CS(true);
119 }
120
121 /*!
122 * brief Configures the DMA transfer attribute.
123 *
124 * This function configures the transfer attribute including the source address,
125 * destination address, transfer size, and so on.
126 * This example shows how to set up the dma_transfer_config_t
127 * parameters and how to call the DMA_ConfigBasicTransfer function.
128 * code
129 * dma_transfer_config_t transferConfig;
130 * memset(&transferConfig, 0, sizeof(transferConfig));
131 * transferConfig.srcAddr = (uint32_t)srcAddr;
132 * transferConfig.destAddr = (uint32_t)destAddr;
133 * transferConfig.enbaleSrcIncrement = true;
134 * transferConfig.enableDestIncrement = true;
135 * transferConfig.srcSize = kDMA_Transfersize32bits;
136 * transferConfig.destSize = kDMA_Transfersize32bits;
137 * transferConfig.transferSize = sizeof(uint32_t) * BUFF_LENGTH;
138 * DMA_SetTransferConfig(DMA0, 0, &transferConfig);
139 * endcode
140 *
141 * param base DMA peripheral base address.
142 * param channel DMA channel number.
143 * param config Pointer to the DMA transfer configuration structure.
144 */
DMA_SetTransferConfig(DMA_Type * base,uint32_t channel,const dma_transfer_config_t * config)145 void DMA_SetTransferConfig(DMA_Type *base, uint32_t channel, const dma_transfer_config_t *config)
146 {
147 assert(channel < (uint32_t)FSL_FEATURE_DMA_MODULE_CHANNEL);
148 assert(config != NULL);
149
150 uint32_t tmpreg;
151
152 /* Set source address */
153 base->DMA[channel].SAR = config->srcAddr;
154 /* Set destination address */
155 base->DMA[channel].DAR = config->destAddr;
156 /* Set transfer bytes */
157 base->DMA[channel].DSR_BCR = DMA_DSR_BCR_BCR(config->transferSize);
158 /* Set DMA Control Register */
159 tmpreg = base->DMA[channel].DCR;
160 tmpreg &= ~(DMA_DCR_DSIZE_MASK | DMA_DCR_DINC_MASK | DMA_DCR_SSIZE_MASK | DMA_DCR_SINC_MASK);
161 tmpreg |= (DMA_DCR_DSIZE(config->destSize) | DMA_DCR_DINC(config->enableDestIncrement) |
162 DMA_DCR_SSIZE(config->srcSize) | DMA_DCR_SINC(config->enableSrcIncrement));
163 base->DMA[channel].DCR = tmpreg;
164 }
165
166 /*!
167 * brief Configures the DMA channel link feature.
168 *
169 * This function allows DMA channels to have their transfers linked. The current DMA channel
170 * triggers a DMA request to the linked channels (LCH1 or LCH2) depending on the channel link
171 * type.
172 * Perform a link to channel LCH1 after each cycle-steal transfer followed by a link to LCH2
173 * after the BCR decrements to 0 if the type is kDMA_ChannelLinkChannel1AndChannel2.
174 * Perform a link to LCH1 after each cycle-steal transfer if the type is kDMA_ChannelLinkChannel1.
175 * Perform a link to LCH1 after the BCR decrements to 0 if the type is kDMA_ChannelLinkChannel1AfterBCR0.
176 *
177 * param base DMA peripheral base address.
178 * param channel DMA channel number.
179 * param config Pointer to the channel link configuration structure.
180 */
DMA_SetChannelLinkConfig(DMA_Type * base,uint32_t channel,const dma_channel_link_config_t * config)181 void DMA_SetChannelLinkConfig(DMA_Type *base, uint32_t channel, const dma_channel_link_config_t *config)
182 {
183 assert(channel < (uint32_t)FSL_FEATURE_DMA_MODULE_CHANNEL);
184 assert(config != NULL);
185
186 uint32_t tmpreg;
187
188 tmpreg = base->DMA[channel].DCR;
189 tmpreg &= ~(DMA_DCR_LINKCC_MASK | DMA_DCR_LCH1_MASK | DMA_DCR_LCH2_MASK);
190 tmpreg |= (DMA_DCR_LINKCC(config->linkType) | DMA_DCR_LCH1(config->channel1) | DMA_DCR_LCH2(config->channel2));
191 base->DMA[channel].DCR = tmpreg;
192 }
193
194 /*!
195 * brief Sets the DMA modulo for the DMA transfer.
196 *
197 * This function defines a specific address range specified to be the value after (SAR + SSIZE)/(DAR + DSIZE)
198 * calculation is performed or the original register value. It provides the ability to implement a circular
199 * data queue easily.
200 *
201 * param base DMA peripheral base address.
202 * param channel DMA channel number.
203 * param srcModulo source address modulo.
204 * param destModulo destination address modulo.
205 */
DMA_SetModulo(DMA_Type * base,uint32_t channel,dma_modulo_t srcModulo,dma_modulo_t destModulo)206 void DMA_SetModulo(DMA_Type *base, uint32_t channel, dma_modulo_t srcModulo, dma_modulo_t destModulo)
207 {
208 assert(channel < (uint32_t)FSL_FEATURE_DMA_MODULE_CHANNEL);
209
210 uint32_t tmpreg;
211
212 tmpreg = base->DMA[channel].DCR;
213 tmpreg &= ~(DMA_DCR_SMOD_MASK | DMA_DCR_DMOD_MASK);
214 tmpreg |= (DMA_DCR_SMOD(srcModulo) | DMA_DCR_DMOD(destModulo));
215 base->DMA[channel].DCR = tmpreg;
216 }
217
218 /*!
219 * brief Creates the DMA handle.
220 *
221 * This function is called first if using the transactional API for the DMA. This function
222 * initializes the internal state of the DMA handle.
223 *
224 * param handle DMA handle pointer. The DMA handle stores callback function and
225 * parameters.
226 * param base DMA peripheral base address.
227 * param channel DMA channel number.
228 */
DMA_CreateHandle(dma_handle_t * handle,DMA_Type * base,uint32_t channel)229 void DMA_CreateHandle(dma_handle_t *handle, DMA_Type *base, uint32_t channel)
230 {
231 assert(handle != NULL);
232 assert(channel < (uint32_t)FSL_FEATURE_DMA_MODULE_CHANNEL);
233
234 uint32_t dmaInstance;
235 uint32_t channelIndex;
236
237 /* Zero the handle */
238 (void)memset(handle, 0, sizeof(*handle));
239
240 handle->base = base;
241 handle->channel = (uint8_t)channel;
242 /* Get the DMA instance number */
243 dmaInstance = DMA_GetInstance(base);
244 channelIndex = (dmaInstance * (uint32_t)FSL_FEATURE_DMA_MODULE_CHANNEL) + channel;
245 /* Store handle */
246 s_DMAHandle[channelIndex] = handle;
247 /* Enable NVIC interrupt. */
248 (void)EnableIRQ(s_dmaIRQNumber[dmaInstance][channelIndex]);
249 }
250
251 /*!
252 * brief Prepares the DMA transfer configuration structure.
253 *
254 * This function prepares the transfer configuration structure according to the user input.
255 * The difference between this function and DMA_PrepareTransfer is that this function expose the address increment
256 * parameter to application, but in DMA_PrepareTransfer, only parts of the address increment option can be selected by
257 * dma_transfer_type_t.
258 *
259 * param config Pointer to the user configuration structure of type dma_transfer_config_t.
260 * param srcAddr DMA transfer source address.
261 * param srcWidth DMA transfer source address width (byte).
262 * param destAddr DMA transfer destination address.
263 * param destWidth DMA transfer destination address width (byte).
264 * param transferBytes DMA transfer bytes to be transferred.
265 * param srcIncrement source address increment type.
266 * param destIncrement dest address increment type.
267 */
DMA_PrepareTransferConfig(dma_transfer_config_t * config,void * srcAddr,uint32_t srcWidth,void * destAddr,uint32_t destWidth,uint32_t transferBytes,dma_addr_increment_t srcIncrement,dma_addr_increment_t destIncrement)268 void DMA_PrepareTransferConfig(dma_transfer_config_t *config,
269 void *srcAddr,
270 uint32_t srcWidth,
271 void *destAddr,
272 uint32_t destWidth,
273 uint32_t transferBytes,
274 dma_addr_increment_t srcIncrement,
275 dma_addr_increment_t destIncrement)
276 {
277 assert(config != NULL);
278 assert(srcAddr != NULL);
279 assert(destAddr != NULL);
280 assert((srcWidth == 1UL) || (srcWidth == 2UL) || (srcWidth == 4UL));
281 assert((destWidth == 1UL) || (destWidth == 2UL) || (destWidth == 4UL));
282
283 /* Initializes the configure structure to zero. */
284 (void)memset(config, 0, sizeof(*config));
285
286 config->srcAddr = (uint32_t)(uint32_t *)srcAddr;
287 config->destAddr = (uint32_t)(uint32_t *)destAddr;
288 config->transferSize = transferBytes;
289
290 if (srcWidth == 1UL)
291 {
292 config->srcSize = kDMA_Transfersize8bits;
293 }
294 else if (srcWidth == 2UL)
295 {
296 config->srcSize = kDMA_Transfersize16bits;
297 }
298 else
299 {
300 config->srcSize = kDMA_Transfersize32bits;
301 }
302
303 if (destWidth == 1UL)
304 {
305 config->destSize = kDMA_Transfersize8bits;
306 }
307 else if (destWidth == 2UL)
308 {
309 config->destSize = kDMA_Transfersize16bits;
310 }
311 else
312 {
313 config->destSize = kDMA_Transfersize32bits;
314 }
315
316 config->enableSrcIncrement = srcIncrement == kDMA_AddrNoIncrement ? false : true;
317 config->enableDestIncrement = destIncrement == kDMA_AddrNoIncrement ? false : true;
318 }
319
320 /*!
321 * brief Prepares the DMA transfer configuration structure.
322 *
323 * This function prepares the transfer configuration structure according to the user input.
324 *
325 * param config Pointer to the user configuration structure of type dma_transfer_config_t.
326 * param srcAddr DMA transfer source address.
327 * param srcWidth DMA transfer source address width (byte).
328 * param destAddr DMA transfer destination address.
329 * param destWidth DMA transfer destination address width (byte).
330 * param transferBytes DMA transfer bytes to be transferred.
331 * param type DMA transfer type.
332 */
DMA_PrepareTransfer(dma_transfer_config_t * config,void * srcAddr,uint32_t srcWidth,void * destAddr,uint32_t destWidth,uint32_t transferBytes,dma_transfer_type_t type)333 void DMA_PrepareTransfer(dma_transfer_config_t *config,
334 void *srcAddr,
335 uint32_t srcWidth,
336 void *destAddr,
337 uint32_t destWidth,
338 uint32_t transferBytes,
339 dma_transfer_type_t type)
340 {
341 assert(config != NULL);
342 assert(srcAddr != NULL);
343 assert(destAddr != NULL);
344 assert((srcWidth == 1UL) || (srcWidth == 2UL) || (srcWidth == 4UL));
345 assert((destWidth == 1UL) || (destWidth == 2UL) || (destWidth == 4UL));
346
347 dma_addr_increment_t srcIncrement = kDMA_AddrNoIncrement, destIncrement = kDMA_AddrNoIncrement;
348
349 if (type == kDMA_MemoryToMemory)
350 {
351 srcIncrement = kDMA_AddrIncrementPerTransferWidth;
352 destIncrement = kDMA_AddrIncrementPerTransferWidth;
353 }
354 else if (type == kDMA_PeripheralToMemory)
355 {
356 srcIncrement = kDMA_AddrNoIncrement;
357 destIncrement = kDMA_AddrIncrementPerTransferWidth;
358 }
359 else
360 {
361 srcIncrement = kDMA_AddrIncrementPerTransferWidth;
362 destIncrement = kDMA_AddrNoIncrement;
363 }
364
365 DMA_PrepareTransferConfig(config, srcAddr, srcWidth, destAddr, destWidth, transferBytes, srcIncrement,
366 destIncrement);
367 }
368
369 /*!
370 * brief Sets the DMA callback function.
371 *
372 * This callback is called in the DMA IRQ handler. Use the callback to do something
373 * after the current transfer complete.
374 *
375 * param handle DMA handle pointer.
376 * param callback DMA callback function pointer.
377 * param userData Parameter for callback function. If it is not needed, just set to NULL.
378 */
DMA_SetCallback(dma_handle_t * handle,dma_callback callback,void * userData)379 void DMA_SetCallback(dma_handle_t *handle, dma_callback callback, void *userData)
380 {
381 assert(handle != NULL);
382
383 handle->callback = callback;
384 handle->userData = userData;
385 }
386
387 /*!
388 * brief Submits the DMA transfer request.
389 *
390 * This function submits the DMA transfer request according to the transfer configuration structure.
391 *
392 * param handle DMA handle pointer.
393 * param config Pointer to DMA transfer configuration structure.
394 * param options Additional configurations for transfer. Use
395 * the defined dma_transfer_options_t type.
396 * retval kStatus_DMA_Success It indicates that the DMA submit transfer request succeeded.
397 * retval kStatus_DMA_Busy It indicates that the DMA is busy. Submit transfer request is not allowed.
398 * note This function can't process multi transfer request.
399 */
DMA_SubmitTransfer(dma_handle_t * handle,const dma_transfer_config_t * config,uint32_t options)400 status_t DMA_SubmitTransfer(dma_handle_t *handle, const dma_transfer_config_t *config, uint32_t options)
401 {
402 assert(handle != NULL);
403 assert(config != NULL);
404
405 /* Check if DMA is busy */
406 if ((handle->base->DMA[handle->channel].DSR_BCR & DMA_DSR_BCR_BSY_MASK) != 0UL)
407 {
408 return kStatus_DMA_Busy;
409 }
410 DMA_ResetChannel(handle->base, handle->channel);
411 DMA_SetTransferConfig(handle->base, handle->channel, config);
412 if ((options & (uint32_t)kDMA_EnableInterrupt) != 0UL)
413 {
414 DMA_EnableInterrupts(handle->base, handle->channel);
415 }
416 return kStatus_Success;
417 }
418
419 /*!
420 * brief DMA aborts a transfer.
421 *
422 * This function disables the channel request and clears all status bits.
423 * Submit another transfer after calling this API.
424 *
425 * param handle DMA handle pointer.
426 */
DMA_AbortTransfer(dma_handle_t * handle)427 void DMA_AbortTransfer(dma_handle_t *handle)
428 {
429 assert(handle != NULL);
430
431 handle->base->DMA[handle->channel].DCR &= ~DMA_DCR_ERQ_MASK;
432 /* clear all status bit */
433 handle->base->DMA[handle->channel].DSR_BCR |= DMA_DSR_BCR_DONE(true);
434 }
435
436 /*!
437 * brief DMA IRQ handler for current transfer complete.
438 *
439 * This function clears the channel interrupt flag and calls
440 * the callback function if it is not NULL.
441 *
442 * param handle DMA handle pointer.
443 */
DMA_HandleIRQ(dma_handle_t * handle)444 void DMA_HandleIRQ(dma_handle_t *handle)
445 {
446 assert(handle != NULL);
447
448 /* Clear interrupt pending bit */
449 DMA_ClearChannelStatusFlags(handle->base, handle->channel, kDMA_TransactionsDoneFlag);
450 if (handle->callback != NULL)
451 {
452 (handle->callback)(handle, handle->userData);
453 }
454 }
455
456 #if defined(FSL_FEATURE_DMA_MODULE_CHANNEL) && (FSL_FEATURE_DMA_MODULE_CHANNEL == 4U)
457 void DMA0_DriverIRQHandler(void);
DMA0_DriverIRQHandler(void)458 void DMA0_DriverIRQHandler(void)
459 {
460 DMA_HandleIRQ(s_DMAHandle[0]);
461 SDK_ISR_EXIT_BARRIER;
462 }
463
464 void DMA1_DriverIRQHandler(void);
DMA1_DriverIRQHandler(void)465 void DMA1_DriverIRQHandler(void)
466 {
467 DMA_HandleIRQ(s_DMAHandle[1]);
468 SDK_ISR_EXIT_BARRIER;
469 }
470
471 void DMA2_DriverIRQHandler(void);
DMA2_DriverIRQHandler(void)472 void DMA2_DriverIRQHandler(void)
473 {
474 DMA_HandleIRQ(s_DMAHandle[2]);
475 SDK_ISR_EXIT_BARRIER;
476 }
477
478 void DMA3_DriverIRQHandler(void);
DMA3_DriverIRQHandler(void)479 void DMA3_DriverIRQHandler(void)
480 {
481 DMA_HandleIRQ(s_DMAHandle[3]);
482 SDK_ISR_EXIT_BARRIER;
483 }
484 #endif /* FSL_FEATURE_DMA_MODULE_CHANNEL */
485