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