1 /***************************************************************************//**
2  * @file
3  * @brief Direct Memory Access (DMA) API
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #ifndef EM_DMA_H
32 #define EM_DMA_H
33 
34 #include "em_device.h"
35 #if defined(DMA_PRESENT)
36 
37 #include <stdio.h>
38 #include <stdbool.h>
39 
40 #ifdef __cplusplus
41 extern "C" {
42 #endif
43 
44 /***************************************************************************//**
45  * @addtogroup dma
46  * @{
47  ******************************************************************************/
48 
49 /*******************************************************************************
50  ********************************   ENUMS   ************************************
51  ******************************************************************************/
52 
53 /**
54  * Amount source/destination address should be incremented for each data
55  * transfer.
56  */
57 typedef enum {
58   dmaDataInc1    = _DMA_CTRL_SRC_INC_BYTE,     /**< Increment address 1 byte. */
59   dmaDataInc2    = _DMA_CTRL_SRC_INC_HALFWORD, /**< Increment address 2 bytes. */
60   dmaDataInc4    = _DMA_CTRL_SRC_INC_WORD,     /**< Increment address 4 bytes. */
61   dmaDataIncNone = _DMA_CTRL_SRC_INC_NONE      /**< Do not increment address. */
62 } DMA_DataInc_TypeDef;
63 
64 /** Data sizes (in number of bytes) to be read/written by DMA transfer. */
65 typedef enum {
66   dmaDataSize1 = _DMA_CTRL_SRC_SIZE_BYTE,     /**< 1 byte DMA transfer size. */
67   dmaDataSize2 = _DMA_CTRL_SRC_SIZE_HALFWORD, /**< 2 byte DMA transfer size. */
68   dmaDataSize4 = _DMA_CTRL_SRC_SIZE_WORD      /**< 4 byte DMA transfer size. */
69 } DMA_DataSize_TypeDef;
70 
71 /** Types of DMA transfer. */
72 typedef enum {
73   /** Basic DMA cycle. */
74   dmaCycleCtrlBasic            = _DMA_CTRL_CYCLE_CTRL_BASIC,
75   /** Auto-request DMA cycle. */
76   dmaCycleCtrlAuto             = _DMA_CTRL_CYCLE_CTRL_AUTO,
77   /** Ping-pong DMA cycle. */
78   dmaCycleCtrlPingPong         = _DMA_CTRL_CYCLE_CTRL_PINGPONG,
79   /** Memory scatter-gather DMA cycle. */
80   dmaCycleCtrlMemScatterGather = _DMA_CTRL_CYCLE_CTRL_MEM_SCATTER_GATHER,
81   /** Peripheral scatter-gather DMA cycle. */
82   dmaCycleCtrlPerScatterGather = _DMA_CTRL_CYCLE_CTRL_PER_SCATTER_GATHER
83 } DMA_CycleCtrl_TypeDef;
84 
85 /** Number of transfers before controller does new arbitration. */
86 typedef enum {
87   dmaArbitrate1    = _DMA_CTRL_R_POWER_1,    /**< Arbitrate after 1 DMA transfer. */
88   dmaArbitrate2    = _DMA_CTRL_R_POWER_2,    /**< Arbitrate after 2 DMA transfers. */
89   dmaArbitrate4    = _DMA_CTRL_R_POWER_4,    /**< Arbitrate after 4 DMA transfers. */
90   dmaArbitrate8    = _DMA_CTRL_R_POWER_8,    /**< Arbitrate after 8 DMA transfers. */
91   dmaArbitrate16   = _DMA_CTRL_R_POWER_16,   /**< Arbitrate after 16 DMA transfers. */
92   dmaArbitrate32   = _DMA_CTRL_R_POWER_32,   /**< Arbitrate after 32 DMA transfers. */
93   dmaArbitrate64   = _DMA_CTRL_R_POWER_64,   /**< Arbitrate after 64 DMA transfers. */
94   dmaArbitrate128  = _DMA_CTRL_R_POWER_128,  /**< Arbitrate after 128 DMA transfers. */
95   dmaArbitrate256  = _DMA_CTRL_R_POWER_256,  /**< Arbitrate after 256 DMA transfers. */
96   dmaArbitrate512  = _DMA_CTRL_R_POWER_512,  /**< Arbitrate after 512 DMA transfers. */
97   dmaArbitrate1024 = _DMA_CTRL_R_POWER_1024  /**< Arbitrate after 1024 DMA transfers. */
98 } DMA_ArbiterConfig_TypeDef;
99 
100 /*******************************************************************************
101  *******************************   STRUCTS   ***********************************
102  ******************************************************************************/
103 
104 /**
105  * @brief
106  *   DMA interrupt callback function pointer.
107  * @details
108  *   Parameters:
109  *   @li channel - DMA channel the callback function is invoked for.
110  *   @li primary - Indicates if callback is invoked for completion of primary
111  *     (true) or alternate (false) descriptor. Mainly useful for
112  *     ping-pong DMA cycles, to know which descriptor to refresh.
113  *   @li user - User definable reference that may be used to pass information
114  *     to be used by the callback handler. If used, referenced data must be
115  *     valid at the point when the interrupt handler invokes callback.
116  *     If callback changes any data in the provided user structure, remember
117  *     that those changes are done in interrupt context and proper protection
118  *     of data may be required.
119  */
120 typedef void (*DMA_FuncPtr_TypeDef)(unsigned int channel, bool primary, void *user);
121 
122 /**
123  * @brief
124  *   Callback structure that can be used to define DMA complete actions.
125  * @details
126  *   A reference to this structure is only stored in the primary descriptor
127  *   for a channel (if using callback feature). If callback is required
128  *   for both primary and alternate descriptor completion, this must be
129  *   handled by one common callback, using the provided 'primary' parameter
130  *   with the callback function.
131  */
132 typedef struct {
133   /**
134    * Pointer to callback function to invoke when DMA transfer cycle is done.
135    * Notice that this function is invoked in interrupt context, and therefore
136    * should be short and non-blocking.
137    */
138   DMA_FuncPtr_TypeDef cbFunc;
139 
140   /** User defined pointer to provide with callback function. */
141   void                *userPtr;
142 
143   /**
144    * For internal use only: Indicates if next callback applies to primary
145    * or alternate descriptor completion. Mainly useful for ping-pong DMA
146    * cycles. Set this value to 0 prior to configuring callback handling.
147    */
148   uint8_t             primary;
149 } DMA_CB_TypeDef;
150 
151 /** Configuration structure for a channel. */
152 typedef struct {
153   /**
154    * Select if channel priority is in the high or default priority group
155    * with respect to arbitration. Within a priority group, lower numbered
156    * channels have higher priority than higher numbered channels.
157    */
158   bool     highPri;
159 
160   /**
161    * Select if interrupt will be enabled for channel (triggering interrupt
162    * handler when dma_done signal is asserted). It should normally be
163    * enabled if using the callback feature for a channel, and disabled if
164    * not using the callback feature.
165    */
166   bool     enableInt;
167 
168   /**
169    * Channel control specifying the source of DMA signals. If accessing
170    * peripherals, use one of the DMAREQ_nnn defines available for the
171    * peripheral. Set to 0 for memory-to-memory DMA cycles.
172    */
173   uint32_t select;
174 
175   /**
176    * @brief
177    *   User definable callback handling configuration.
178    * @details
179    *   Refer to structure definition for details. The callback
180    *   is invoked when specified DMA cycle is complete (when dma_done
181    *   signal asserted). Callback is invoked in interrupt context,
182    *   and should be efficient and non-blocking. Set to NULL to not
183    *   use the callback feature.
184    * @note
185    *   Referenced structure is used by the interrupt handler, and must
186    *   be available until no longer used. Thus, in most cases it should
187    *   not be located on the stack.
188    */
189   DMA_CB_TypeDef *cb;
190 } DMA_CfgChannel_TypeDef;
191 
192 /**
193  * Configuration structure for primary or alternate descriptor
194  * (not used for scatter-gather DMA cycles).
195  */
196 typedef struct {
197   /** Destination increment size for each DMA transfer. */
198   DMA_DataInc_TypeDef       dstInc;
199 
200   /** Source increment size for each DMA transfer. */
201   DMA_DataInc_TypeDef       srcInc;
202 
203   /** DMA transfer unit size. */
204   DMA_DataSize_TypeDef      size;
205 
206   /**
207    * Arbitration rate, i.e., number of DMA transfers done before re-arbitration
208    * takes place.
209    */
210   DMA_ArbiterConfig_TypeDef arbRate;
211 
212   /**
213    * HPROT signal state, refer to reference manual, DMA chapter for
214    * further details. Normally set to 0 if protection is not an issue.
215    * The following bits are available:
216    * @li bit 0 - HPROT[1] control for source read accesses,
217    *   privileged/non-privileged access.
218    * @li bit 3 - HPROT[1] control for destination write accesses,
219    *   privileged/non-privileged access.
220    */
221   uint8_t hprot;
222 } DMA_CfgDescr_TypeDef;
223 
224 #if defined(_DMA_LOOP0_MASK) && defined(_DMA_LOOP1_MASK)
225 /**
226  * Configuration structure for loop mode.
227  */
228 typedef struct {
229   /** Enable repeated loop. */
230   bool      enable;
231   /** Width of transfer, reload value for nMinus1. */
232   uint16_t  nMinus1;
233 } DMA_CfgLoop_TypeDef;
234 #endif
235 
236 #if defined(_DMA_RECT0_MASK)
237 /**
238  * Configuration structure for rectangular copy.
239  */
240 typedef struct {
241   /** DMA channel destination stride (width of destination image, distance between lines). */
242   uint16_t dstStride;
243   /** DMA channel source stride (width of source image, distance between lines). */
244   uint16_t srcStride;
245   /** 2D copy height. */
246   uint16_t height;
247 } DMA_CfgRect_TypeDef;
248 #endif
249 
250 /** Configuration structure for alternate scatter-gather descriptor. */
251 typedef struct {
252   /** Pointer to location to transfer data from. */
253   void                      *src;
254 
255   /** Pointer to location to transfer data to. */
256   void                      *dst;
257 
258   /** Destination increment size for each DMA transfer. */
259   DMA_DataInc_TypeDef       dstInc;
260 
261   /** Source increment size for each DMA transfer. */
262   DMA_DataInc_TypeDef       srcInc;
263 
264   /** DMA transfer unit size. */
265   DMA_DataSize_TypeDef      size;
266 
267   /**
268    * Arbitration rate, i.e., number of DMA transfers done before re-arbitration
269    * takes place.
270    */
271   DMA_ArbiterConfig_TypeDef arbRate;
272 
273   /** Number of DMA transfers minus 1 to do. Must be <= 1023. */
274   uint16_t                  nMinus1;
275 
276   /**
277    * HPROT signal state, refer to reference manual, DMA chapter for
278    * further details. Normally set to 0 if protection is not an issue.
279    * The following bits are available:
280    * @li bit 0 - HPROT[1] control for source read accesses,
281    *   privileged/non-privileged access.
282    * @li bit 3 - HPROT[1] control for destination write accesses,
283    *   privileged/non-privileged access.
284    */
285   uint8_t hprot;
286 
287   /** Specify if a memory or peripheral scatter-gather DMA cycle. Notice
288    *  that this parameter should be the same for all alternate
289    *  descriptors.
290    *  @li true - this is a peripheral scatter-gather cycle.
291    *  @li false - this is a memory scatter-gather cycle.
292    */
293   bool    peripheral;
294 } DMA_CfgDescrSGAlt_TypeDef;
295 
296 /** DMA initialization structure. */
297 typedef struct {
298   /**
299    * HPROT signal state when accessing the primary/alternate
300    * descriptors. Normally set to 0 if protection is not an issue.
301    * The following bits are available:
302    * @li bit 0 - HPROT[1] control for descriptor accesses (i.e., when
303    *   the DMA controller accesses the channel control block itself),
304    *   privileged/non-privileged access.
305    */
306   uint8_t hprot;
307 
308   /**
309    * Pointer to the control block in memory holding descriptors (channel
310    * control data structures). This memory must be properly aligned
311    * at a 256 bytes, i.e., the 8 least significant bits must be zero.
312    *
313    * Refer to the reference manual, DMA chapter for more details.
314    *
315    * It is possible to provide a smaller memory block, only covering
316    * those channels actually used, if not all available channels are used.
317    * For instance, if only using 4 channels (0-3), both primary and alternate
318    * structures, then only 16*2*4 = 128 bytes must be provided. However, this
319    * implementation has no check if later exceeding such a limit
320    * by configuring for instance channel 4, in which case memory overwrite
321    * of some other data will occur.
322    */
323   DMA_DESCRIPTOR_TypeDef *controlBlock;
324 } DMA_Init_TypeDef;
325 
326 /*******************************************************************************
327  *****************************   PROTOTYPES   **********************************
328  ******************************************************************************/
329 
330 void DMA_ActivateAuto(unsigned int channel,
331                       bool primary,
332                       void *dst,
333                       const void *src,
334                       unsigned int nMinus1);
335 void DMA_ActivateBasic(unsigned int channel,
336                        bool primary,
337                        bool useBurst,
338                        void *dst,
339                        const void *src,
340                        unsigned int nMinus1);
341 void DMA_ActivatePingPong(unsigned int channel,
342                           bool useBurst,
343                           void *primDst,
344                           const void *primSrc,
345                           unsigned int primNMinus1,
346                           void *altDst,
347                           const void *altSrc,
348                           unsigned int altNMinus1);
349 void DMA_ActivateScatterGather(unsigned int channel,
350                                bool useBurst,
351                                DMA_DESCRIPTOR_TypeDef *altDescr,
352                                unsigned int count);
353 void DMA_CfgChannel(unsigned int channel, DMA_CfgChannel_TypeDef *cfg);
354 void DMA_CfgDescr(unsigned int channel,
355                   bool primary,
356                   DMA_CfgDescr_TypeDef *cfg);
357 #if defined(_DMA_LOOP0_MASK) && defined(_DMA_LOOP1_MASK)
358 void DMA_CfgLoop(unsigned int channel, DMA_CfgLoop_TypeDef *cfg);
359 #endif
360 
361 #if defined(_DMA_RECT0_MASK)
362 void DMA_CfgRect(unsigned int channel, DMA_CfgRect_TypeDef *cfg);
363 #endif
364 
365 #if defined(_DMA_LOOP0_MASK) && defined(_DMA_LOOP1_MASK)
366 /***************************************************************************//**
367  * @brief
368  *   Clear Loop configuration for channel.
369  *
370  * @param[in] channel
371  *   Channel to reset loop configuration for.
372  ******************************************************************************/
DMA_ResetLoop(unsigned int channel)373 __STATIC_INLINE void DMA_ResetLoop(unsigned int channel)
374 {
375   /* Clean loop copy operation */
376   switch (channel) {
377     case 0:
378       DMA->LOOP0 = _DMA_LOOP0_RESETVALUE;
379       break;
380     case 1:
381       DMA->LOOP1 = _DMA_LOOP1_RESETVALUE;
382       break;
383     default:
384       break;
385   }
386 }
387 #endif
388 
389 #if defined(_DMA_RECT0_MASK)
390 /***************************************************************************//**
391  * @brief
392  *   Clear Rect/2D DMA configuration for channel.
393  *
394  * @param[in] channel
395  *   Channel to reset loop configuration for.
396  ******************************************************************************/
DMA_ResetRect(unsigned int channel)397 __STATIC_INLINE void DMA_ResetRect(unsigned int channel)
398 {
399   (void) channel;
400 
401   /* Clear rect copy operation. */
402   DMA->RECT0 = _DMA_RECT0_RESETVALUE;
403 }
404 #endif
405 void DMA_CfgDescrScatterGather(DMA_DESCRIPTOR_TypeDef *descr,
406                                unsigned int indx,
407                                DMA_CfgDescrSGAlt_TypeDef *cfg);
408 void DMA_ChannelEnable(unsigned int channel, bool enable);
409 bool DMA_ChannelEnabled(unsigned int channel);
410 void DMA_ChannelRequestEnable(unsigned int channel, bool enable);
411 void DMA_Init(DMA_Init_TypeDef *init);
412 void DMA_IRQHandler(void);
413 void DMA_RefreshPingPong(unsigned int channel,
414                          bool primary,
415                          bool useBurst,
416                          void *dst,
417                          const void *src,
418                          unsigned int nMinus1,
419                          bool last);
420 void DMA_Reset(void);
421 
422 /***************************************************************************//**
423  * @brief
424  *   Clear one or more pending DMA interrupts.
425  *
426  * @param[in] flags
427  *   Pending DMA interrupt sources to clear. Use one or more valid
428  *   interrupt flags for the DMA module (DMA_IFC_nnn).
429  ******************************************************************************/
DMA_IntClear(uint32_t flags)430 __STATIC_INLINE void DMA_IntClear(uint32_t flags)
431 {
432   DMA->IFC = flags;
433 }
434 
435 /***************************************************************************//**
436  * @brief
437  *   Disable one or more DMA interrupts.
438  *
439  * @param[in] flags
440  *   DMA interrupt sources to disable. Use one or more valid
441  *   interrupt flags for the DMA module (DMA_IEN_nnn).
442  ******************************************************************************/
DMA_IntDisable(uint32_t flags)443 __STATIC_INLINE void DMA_IntDisable(uint32_t flags)
444 {
445   DMA->IEN &= ~flags;
446 }
447 
448 /***************************************************************************//**
449  * @brief
450  *   Enable one or more DMA interrupts.
451  *
452  * @note
453  *   Depending on the use, a pending interrupt may already be set prior to
454  *   enabling the interrupt. To ignore a pending interrupt, consider using
455  *   DMA_IntClear() prior to enabling the interrupt.
456  *
457  * @param[in] flags
458  *   DMA interrupt sources to enable. Use one or more valid
459  *   interrupt flags for the DMA module (DMA_IEN_nnn).
460  ******************************************************************************/
DMA_IntEnable(uint32_t flags)461 __STATIC_INLINE void DMA_IntEnable(uint32_t flags)
462 {
463   DMA->IEN |= flags;
464 }
465 
466 /***************************************************************************//**
467  * @brief
468  *   Get pending DMA interrupt flags.
469  *
470  * @note
471  *   Event bits are not cleared by the use of this function.
472  *
473  * @return
474  *   DMA interrupt sources pending. Returns one or more valid
475  *   interrupt flags for the DMA module (DMA_IF_nnn).
476  ******************************************************************************/
DMA_IntGet(void)477 __STATIC_INLINE uint32_t DMA_IntGet(void)
478 {
479   return DMA->IF;
480 }
481 
482 /***************************************************************************//**
483  * @brief
484  *   Get enabled and pending DMA interrupt flags.
485  *   Useful for handling more interrupt sources in the same interrupt handler.
486  *
487  * @note
488  *   Interrupt flags are not cleared by the use of this function.
489  *
490  * @return
491  *   Pending and enabled DMA interrupt sources
492  *   Return value is the bitwise AND of
493  *   - the enabled interrupt sources in DMA_IEN and
494  *   - the pending interrupt flags DMA_IF.
495  ******************************************************************************/
DMA_IntGetEnabled(void)496 __STATIC_INLINE uint32_t DMA_IntGetEnabled(void)
497 {
498   uint32_t ien;
499 
500   ien = DMA->IEN;
501   return DMA->IF & ien;
502 }
503 
504 /***************************************************************************//**
505  * @brief
506  *   Set one or more pending DMA interrupts.
507  *
508  * @param[in] flags
509  *   DMA interrupt sources to set to pending. Use one or more valid
510  *   interrupt flags for the DMA module (DMA_IFS_nnn).
511  ******************************************************************************/
DMA_IntSet(uint32_t flags)512 __STATIC_INLINE void DMA_IntSet(uint32_t flags)
513 {
514   DMA->IFS = flags;
515 }
516 
517 /** @} (end addtogroup dma) */
518 
519 #ifdef __cplusplus
520 }
521 #endif
522 
523 #endif /* defined( DMA_PRESENT ) */
524 #endif /* EM_DMA_H */
525