1 /***************************************************************************//**
2  * @file
3  * @brief DMADRV API implementation.
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 #include <stdbool.h>
32 #include <stddef.h>
33 
34 #include "em_device.h"
35 #include "sl_core.h"
36 
37 #include "dmadrv.h"
38 
39 #if defined(EMDRV_DMADRV_LDMA_S3)
40 #include "sl_clock_manager.h"
41 #endif
42 
43 /// @cond DO_NOT_INCLUDE_WITH_DOXYGEN
44 
45 #if !defined(EMDRV_DMADRV_DMA_CH_COUNT) \
46   || (EMDRV_DMADRV_DMA_CH_COUNT > DMA_CHAN_COUNT)
47 
48 #if defined(_SILICON_LABS_32B_SERIES_3)
49 #define EMDRV_DMADRV_DMA_CH_COUNT DMA_CHAN_COUNT(0)
50 #else
51 #define EMDRV_DMADRV_DMA_CH_COUNT DMA_CHAN_COUNT
52 #endif
53 #endif
54 
55 typedef enum {
56   dmaDirectionMemToPeripheral,
57   dmaDirectionPeripheralToMem
58 } DmaDirection_t;
59 
60 typedef enum {
61   dmaModeBasic,
62   dmaModePingPong
63 } DmaMode_t;
64 
65 typedef struct {
66   DMADRV_Callback_t callback;
67   void              *userParam;
68   unsigned int      callbackCount;
69 #if defined(EMDRV_DMADRV_UDMA)
70   int               length;
71 #endif
72   bool              allocated;
73 #if defined(EMDRV_DMADRV_LDMA) || defined(EMDRV_DMADRV_LDMA_S3)
74   DmaMode_t         mode;
75 #endif
76 } ChTable_t;
77 
78 static bool initialized = false;
79 static ChTable_t chTable[EMDRV_DMADRV_DMA_CH_COUNT];
80 
81 #if defined(EMDRV_DMADRV_UDMA)
82 static DMA_CB_TypeDef dmaCallBack[EMDRV_DMADRV_DMA_CH_COUNT];
83 #endif
84 
85 #if defined(EMDRV_DMADRV_LDMA) || defined(EMDRV_DMADRV_LDMA_S3)
86 #if defined(EMDRV_DMADRV_LDMA)
87 const LDMA_TransferCfg_t xferCfgPeripheral = LDMA_TRANSFER_CFG_PERIPHERAL(0);
88 const LDMA_Descriptor_t m2p = LDMA_DESCRIPTOR_SINGLE_M2P_BYTE(NULL, NULL, 1UL);
89 const LDMA_Descriptor_t p2m = LDMA_DESCRIPTOR_SINGLE_P2M_BYTE(NULL, NULL, 1UL);
90 
91 typedef struct {
92   LDMA_Descriptor_t desc[2];
93 } DmaXfer_t;
94 #else
95 const sl_hal_ldma_transfer_config_t xferCfgPeripheral = SL_HAL_LDMA_TRANSFER_CFG_PERIPHERAL(0);
96 const sl_hal_ldma_descriptor_t m2p = SL_HAL_LDMA_DESCRIPTOR_SINGLE_M2P(SL_HAL_LDMA_CTRL_SIZE_BYTE, NULL, NULL, 1UL);
97 const sl_hal_ldma_descriptor_t p2m = SL_HAL_LDMA_DESCRIPTOR_SINGLE_P2M(SL_HAL_LDMA_CTRL_SIZE_BYTE, NULL, NULL, 1UL);
98 
99 typedef struct {
100   sl_hal_ldma_descriptor_t desc[2];
101 } DmaXfer_t;
102 #endif
103 
104 static DmaXfer_t dmaXfer[EMDRV_DMADRV_DMA_CH_COUNT];
105 #endif
106 
107 static Ecode_t StartTransfer(DmaMode_t                 mode,
108                              DmaDirection_t            direction,
109                              unsigned int              channelId,
110                              DMADRV_PeripheralSignal_t peripheralSignal,
111                              void                      *buf0,
112                              void                      *buf1,
113                              void                      *buf2,
114                              bool                      bufInc,
115                              int                       len,
116                              DMADRV_DataSize_t         size,
117                              DMADRV_Callback_t         callback,
118                              void                      *cbUserParam);
119 
120 #if defined(EMDRV_DMADRV_LDMA_S3)
121 static void LDMA_IRQHandlerDefault(uint8_t chnum);
122 #endif
123 
124 /// @endcond
125 
126 /***************************************************************************//**
127  * @brief
128  *  Allocate (reserve) a DMA channel.
129  *
130  * @param[out] channelId
131  *  The channel ID assigned by DMADRV.
132  *
133  * @param[in] capabilities
134  *  Not used.
135  *
136  * @return
137  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
138  *  DMADRV @ref Ecode_t is returned.
139  ******************************************************************************/
DMADRV_AllocateChannel(unsigned int * channelId,void * capabilities)140 Ecode_t DMADRV_AllocateChannel(unsigned int *channelId, void *capabilities)
141 {
142   unsigned int i;
143   (void)capabilities;
144   CORE_DECLARE_IRQ_STATE;
145 
146   if ( !initialized ) {
147     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
148   }
149 
150   if ( channelId == NULL ) {
151     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
152   }
153 
154   CORE_ENTER_ATOMIC();
155   for ( i = 0U; i < (unsigned int)EMDRV_DMADRV_DMA_CH_COUNT; i++ ) {
156     if ( !chTable[i].allocated ) {
157       *channelId           = i;
158       chTable[i].allocated = true;
159       chTable[i].callback  = NULL;
160       CORE_EXIT_ATOMIC();
161       return ECODE_EMDRV_DMADRV_OK;
162     }
163   }
164   CORE_EXIT_ATOMIC();
165   return ECODE_EMDRV_DMADRV_CHANNELS_EXHAUSTED;
166 }
167 
168 /***************************************************************************//**
169  * @brief
170  *  Allocate (reserve) the given DMA channel if he is free.
171  *
172  * @param[out] channelId
173  *  The channel ID to be assigned by DMADRV.
174  *
175  * @param[in] capabilities
176  *  Not used.
177  *
178  * @return
179  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
180  *  DMADRV @ref Ecode_t is returned.
181  ******************************************************************************/
DMADRV_AllocateChannelById(unsigned int channelId,void * capabilities)182 Ecode_t DMADRV_AllocateChannelById(unsigned int channelId, void *capabilities)
183 {
184   (void)capabilities;
185   CORE_DECLARE_IRQ_STATE;
186 
187   if ( !initialized ) {
188     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
189   }
190 
191   if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) {
192     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
193   }
194 
195   CORE_ENTER_ATOMIC();
196   if ( !chTable[channelId].allocated ) {
197     chTable[channelId].allocated = true;
198     chTable[channelId].callback  = NULL;
199     CORE_EXIT_ATOMIC();
200     return ECODE_EMDRV_DMADRV_OK;
201   }
202   CORE_EXIT_ATOMIC();
203   return ECODE_EMDRV_DMADRV_IN_USE;
204 }
205 
206 /***************************************************************************//**
207  * @brief
208  *  Deinitialize DMADRV.
209  *
210  * @details
211  *  If DMA channels are not currently allocated, it will disable DMA hardware
212  *  and mask associated interrupts.
213  *
214  * @return
215  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
216  *  DMADRV @ref Ecode_t is returned.
217  ******************************************************************************/
DMADRV_DeInit(void)218 Ecode_t DMADRV_DeInit(void)
219 {
220   int i;
221   bool inUse;
222   CORE_DECLARE_IRQ_STATE;
223 
224   inUse = false;
225 
226   CORE_ENTER_ATOMIC();
227   for ( i = 0; i < (int)EMDRV_DMADRV_DMA_CH_COUNT; i++ ) {
228     if ( chTable[i].allocated ) {
229       inUse = true;
230       break;
231     }
232   }
233 
234   if ( !inUse ) {
235 #if defined(EMDRV_DMADRV_LDMA)
236     LDMA_DeInit();
237 #elif defined(EMDRV_DMADRV_LDMA_S3)
238     NVIC_DisableIRQ(LDMA0_CHNL0_IRQn);
239     NVIC_DisableIRQ(LDMA0_CHNL1_IRQn);
240     NVIC_DisableIRQ(LDMA0_CHNL2_IRQn);
241     NVIC_DisableIRQ(LDMA0_CHNL3_IRQn);
242     NVIC_DisableIRQ(LDMA0_CHNL4_IRQn);
243     NVIC_DisableIRQ(LDMA0_CHNL5_IRQn);
244     NVIC_DisableIRQ(LDMA0_CHNL6_IRQn);
245     NVIC_DisableIRQ(LDMA0_CHNL7_IRQn);
246 
247     sl_hal_ldma_reset(LDMA0);
248 
249     sl_clock_manager_disable_bus_clock(SL_BUS_CLOCK_LDMA0);
250     sl_clock_manager_disable_bus_clock(SL_BUS_CLOCK_LDMAXBAR0);
251 #endif
252 
253     initialized = false;
254     CORE_EXIT_ATOMIC();
255     return ECODE_EMDRV_DMADRV_OK;
256   }
257   CORE_EXIT_ATOMIC();
258 
259   return ECODE_EMDRV_DMADRV_IN_USE;
260 }
261 
262 /***************************************************************************//**
263  * @brief
264  *  Free an allocated (reserved) DMA channel.
265  *
266  * @param[in] channelId
267  *  The channel ID to free.
268  *
269  * @return
270  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
271  *  DMADRV @ref Ecode_t is returned.
272  ******************************************************************************/
DMADRV_FreeChannel(unsigned int channelId)273 Ecode_t DMADRV_FreeChannel(unsigned int channelId)
274 {
275   CORE_DECLARE_IRQ_STATE;
276 
277   if ( !initialized ) {
278     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
279   }
280 
281   if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) {
282     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
283   }
284 
285   CORE_ENTER_ATOMIC();
286   if ( chTable[channelId].allocated ) {
287     chTable[channelId].allocated = false;
288     CORE_EXIT_ATOMIC();
289     return ECODE_EMDRV_DMADRV_OK;
290   }
291   CORE_EXIT_ATOMIC();
292 
293   return ECODE_EMDRV_DMADRV_ALREADY_FREED;
294 }
295 
296 /***************************************************************************//**
297  * @brief
298  *  Initialize DMADRV.
299  *
300  * @details
301  *  The DMA hardware is initialized.
302  *
303  * @return
304  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
305  *  DMADRV @ref Ecode_t is returned.
306  ******************************************************************************/
DMADRV_Init(void)307 Ecode_t DMADRV_Init(void)
308 {
309   int i;
310   CORE_DECLARE_IRQ_STATE;
311 #if defined(EMDRV_DMADRV_UDMA)
312   DMA_Init_TypeDef dmaInit;
313 #elif defined(EMDRV_DMADRV_LDMA)
314   LDMA_Init_t dmaInit = LDMA_INIT_DEFAULT;
315   dmaInit.ldmaInitCtrlNumFixed = EMDRV_DMADRV_DMA_CH_PRIORITY;
316 #elif defined(EMDRV_DMADRV_LDMA_S3)
317   sl_hal_ldma_config_t dmaInit = SL_HAL_LDMA_INIT_DEFAULT;
318   dmaInit.num_fixed_priority = EMDRV_DMADRV_DMA_CH_PRIORITY;
319 #endif
320 
321   CORE_ENTER_ATOMIC();
322   if ( initialized ) {
323     CORE_EXIT_ATOMIC();
324     return ECODE_EMDRV_DMADRV_ALREADY_INITIALIZED;
325   }
326   initialized = true;
327   CORE_EXIT_ATOMIC();
328 
329   if ( EMDRV_DMADRV_DMA_IRQ_PRIORITY >= (1 << __NVIC_PRIO_BITS) ) {
330     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
331   }
332 
333   for ( i = 0; i < (int)EMDRV_DMADRV_DMA_CH_COUNT; i++ ) {
334     chTable[i].allocated = false;
335   }
336 
337 #if defined(EMDRV_DMADRV_UDMA)
338   NVIC_SetPriority(DMA_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
339   dmaInit.hprot        = 0;
340   dmaInit.controlBlock = dmaControlBlock;
341   DMA_Init(&dmaInit);
342 #elif defined(EMDRV_DMADRV_LDMA)
343   dmaInit.ldmaInitIrqPriority = EMDRV_DMADRV_DMA_IRQ_PRIORITY;
344   LDMA_Init(&dmaInit);
345 #elif defined(EMDRV_DMADRV_LDMA_S3)
346   sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_LDMA0);
347   sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_LDMAXBAR0);
348   sl_hal_ldma_init(LDMA0, &dmaInit);
349 
350   NVIC_ClearPendingIRQ(LDMA0_CHNL0_IRQn);
351   NVIC_ClearPendingIRQ(LDMA0_CHNL1_IRQn);
352   NVIC_ClearPendingIRQ(LDMA0_CHNL2_IRQn);
353   NVIC_ClearPendingIRQ(LDMA0_CHNL3_IRQn);
354   NVIC_ClearPendingIRQ(LDMA0_CHNL4_IRQn);
355   NVIC_ClearPendingIRQ(LDMA0_CHNL5_IRQn);
356   NVIC_ClearPendingIRQ(LDMA0_CHNL6_IRQn);
357   NVIC_ClearPendingIRQ(LDMA0_CHNL7_IRQn);
358 
359   NVIC_SetPriority(LDMA0_CHNL0_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
360   NVIC_SetPriority(LDMA0_CHNL1_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
361   NVIC_SetPriority(LDMA0_CHNL2_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
362   NVIC_SetPriority(LDMA0_CHNL3_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
363   NVIC_SetPriority(LDMA0_CHNL4_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
364   NVIC_SetPriority(LDMA0_CHNL5_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
365   NVIC_SetPriority(LDMA0_CHNL6_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
366   NVIC_SetPriority(LDMA0_CHNL7_IRQn, EMDRV_DMADRV_DMA_IRQ_PRIORITY);
367 
368   NVIC_EnableIRQ(LDMA0_CHNL0_IRQn);
369   NVIC_EnableIRQ(LDMA0_CHNL1_IRQn);
370   NVIC_EnableIRQ(LDMA0_CHNL2_IRQn);
371   NVIC_EnableIRQ(LDMA0_CHNL3_IRQn);
372   NVIC_EnableIRQ(LDMA0_CHNL4_IRQn);
373   NVIC_EnableIRQ(LDMA0_CHNL5_IRQn);
374   NVIC_EnableIRQ(LDMA0_CHNL6_IRQn);
375   NVIC_EnableIRQ(LDMA0_CHNL7_IRQn);
376 
377   sl_hal_ldma_enable(LDMA0);
378 #endif
379 
380   return ECODE_EMDRV_DMADRV_OK;
381 }
382 
383 #if defined(EMDRV_DMADRV_LDMA) || defined(DOXYGEN)
384 /***************************************************************************//**
385  * @brief
386  *  Start an LDMA transfer.
387  *
388  * @details
389  *  This function is similar to the emlib LDMA function.
390  *
391  * @param[in] channelId
392  *  The channel ID to use.
393  *
394  * @param[in] transfer
395  *  A DMA transfer configuration data structure.
396  *
397  * @param[in] descriptor
398  *  A DMA transfer descriptor, can be an array of descriptors linked together.
399  *
400  * @param[in] callback
401  *  An optional callback function for signalling completion. May be NULL if not
402  *  needed.
403  *
404  * @param[in] cbUserParam
405  *  An optional user parameter to feed to the callback function. May be NULL if
406  *  not needed.
407  *
408  * @return
409  *   @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
410  *   DMADRV @ref Ecode_t is returned.
411  ******************************************************************************/
DMADRV_LdmaStartTransfer(int channelId,LDMA_TransferCfg_t * transfer,LDMA_Descriptor_t * descriptor,DMADRV_Callback_t callback,void * cbUserParam)412 Ecode_t DMADRV_LdmaStartTransfer(int                channelId,
413                                  LDMA_TransferCfg_t *transfer,
414                                  LDMA_Descriptor_t  *descriptor,
415                                  DMADRV_Callback_t  callback,
416                                  void               *cbUserParam)
417 {
418   ChTable_t *ch;
419 
420   if ( !initialized ) {
421     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
422   }
423 
424   if ( channelId >= (int)EMDRV_DMADRV_DMA_CH_COUNT ) {
425     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
426   }
427 
428   ch = &chTable[channelId];
429   if ( ch->allocated == false ) {
430     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
431   }
432 
433   ch->callback      = callback;
434   ch->userParam     = cbUserParam;
435   ch->callbackCount = 0;
436   LDMA_StartTransfer(channelId, transfer, descriptor);
437 
438   return ECODE_EMDRV_DMADRV_OK;
439 }
440 #elif defined(EMDRV_DMADRV_LDMA_S3)
DMADRV_LdmaStartTransfer(int channelId,sl_hal_ldma_transfer_config_t * transfer,sl_hal_ldma_descriptor_t * descriptor,DMADRV_Callback_t callback,void * cbUserParam)441 Ecode_t DMADRV_LdmaStartTransfer(int                            channelId,
442                                  sl_hal_ldma_transfer_config_t  *transfer,
443                                  sl_hal_ldma_descriptor_t       *descriptor,
444                                  DMADRV_Callback_t              callback,
445                                  void                           *cbUserParam)
446 {
447   ChTable_t *ch;
448 
449   if ( !initialized ) {
450     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
451   }
452 
453   if ( channelId >= (int)EMDRV_DMADRV_DMA_CH_COUNT ) {
454     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
455   }
456 
457   ch = &chTable[channelId];
458   if ( ch->allocated == false ) {
459     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
460   }
461 
462   ch->callback      = callback;
463   ch->userParam     = cbUserParam;
464   ch->callbackCount = 0;
465   sl_hal_ldma_init_transfer(LDMA0, channelId, transfer, descriptor);
466   sl_hal_ldma_enable_interrupts(LDMA0, (1 << channelId));
467   sl_hal_ldma_start_transfer(LDMA0, channelId);
468 
469   return ECODE_EMDRV_DMADRV_OK;
470 }
471 #endif
472 
473 /***************************************************************************//**
474  * @brief
475  *  Start a memory to a peripheral DMA transfer.
476  *
477  * @param[in] channelId
478  *  The channel ID to use for the transfer.
479  *
480  * @param[in] peripheralSignal
481  *  Selects which peripheral/peripheralsignal to use.
482  *
483  * @param[in] dst
484  *  A destination (peripheral register) memory address.
485  *
486  * @param[in] src
487  *  A source memory address.
488  *
489  * @param[in] srcInc
490  *  Set to true to enable source address increment (increments according to
491  *  @a size parameter).
492  *
493  * @param[in] len
494  *  A number of items (of @a size size) to transfer.
495  *
496  * @param[in] size
497  *  An item size, byte, halfword or word.
498  *
499  * @param[in] callback
500  *  A function to call on DMA completion, use NULL if not needed.
501  *
502  * @param[in] cbUserParam
503  *  An optional user parameter to feed to the callback function. Use NULL if
504  *  not needed.
505  *
506  * @return
507  *   @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
508  *   DMADRV @ref Ecode_t is returned.
509  ******************************************************************************/
DMADRV_MemoryPeripheral(unsigned int channelId,DMADRV_PeripheralSignal_t peripheralSignal,void * dst,void * src,bool srcInc,int len,DMADRV_DataSize_t size,DMADRV_Callback_t callback,void * cbUserParam)510 Ecode_t DMADRV_MemoryPeripheral(unsigned int          channelId,
511                                 DMADRV_PeripheralSignal_t
512                                 peripheralSignal,
513                                 void                  *dst,
514                                 void                  *src,
515                                 bool                  srcInc,
516                                 int                   len,
517                                 DMADRV_DataSize_t     size,
518                                 DMADRV_Callback_t     callback,
519                                 void                  *cbUserParam)
520 {
521   return StartTransfer(dmaModeBasic,
522                        dmaDirectionMemToPeripheral,
523                        channelId,
524                        peripheralSignal,
525                        dst,
526                        src,
527                        NULL,
528                        srcInc,
529                        len,
530                        size,
531                        callback,
532                        cbUserParam);
533 }
534 
535 /***************************************************************************//**
536  * @brief
537  *  Start a memory to a peripheral ping-pong DMA transfer.
538  *
539  * @param[in] channelId
540  *  The channel ID to use for the transfer.
541  *
542  * @param[in] peripheralSignal
543  *  Selects which peripheral/peripheralsignal to use.
544  *
545  * @param[in] dst
546  *  A destination (peripheral register) memory address.
547  *
548  * @param[in] src0
549  *  A source memory address of the first (ping) buffer.
550  *
551  * @param[in] src1
552  *  A source memory address of the second (pong) buffer.
553  *
554  * @param[in] srcInc
555  *  Set to true to enable source address increment (increments according to
556  *  @a size parameter).
557  *
558  * @param[in] len
559  *  A number of items (of @a size size) to transfer.
560  *
561  * @param[in] size
562  *  An item size, byte, halfword or word.
563  *
564  * @param[in] callback
565  *  A function to call on DMA completion, use NULL if not needed.
566  *
567  * @param[in] cbUserParam
568  *  An optional user parameter to feed to the callback function. Use NULL if
569  *  not needed.
570  *
571  * @return
572  *   @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
573  *   DMADRV @ref Ecode_t is returned.
574  ******************************************************************************/
DMADRV_MemoryPeripheralPingPong(unsigned int channelId,DMADRV_PeripheralSignal_t peripheralSignal,void * dst,void * src0,void * src1,bool srcInc,int len,DMADRV_DataSize_t size,DMADRV_Callback_t callback,void * cbUserParam)575 Ecode_t DMADRV_MemoryPeripheralPingPong(
576   unsigned int          channelId,
577   DMADRV_PeripheralSignal_t
578   peripheralSignal,
579   void                  *dst,
580   void                  *src0,
581   void                  *src1,
582   bool                  srcInc,
583   int                   len,
584   DMADRV_DataSize_t     size,
585   DMADRV_Callback_t     callback,
586   void                  *cbUserParam)
587 {
588   return StartTransfer(dmaModePingPong,
589                        dmaDirectionMemToPeripheral,
590                        channelId,
591                        peripheralSignal,
592                        dst,
593                        src0,
594                        src1,
595                        srcInc,
596                        len,
597                        size,
598                        callback,
599                        cbUserParam);
600 }
601 
602 /***************************************************************************//**
603  * @brief
604  *  Start a peripheral to memory DMA transfer.
605  *
606  * @param[in] channelId
607  *  The channel ID to use for the transfer.
608  *
609  * @param[in] peripheralSignal
610  *  Selects which peripheral/peripheralsignal to use.
611  *
612  * @param[in] dst
613  *  A destination memory address.
614  *
615  * @param[in] src
616  *  A source memory (peripheral register) address.
617  *
618  * @param[in] dstInc
619  *  Set to true to enable destination address increment (increments according
620  *  to @a size parameter).
621  *
622  * @param[in] len
623  *  A number of items (of @a size size) to transfer.
624  *
625  * @param[in] size
626  *  An item size, byte, halfword or word.
627  *
628  * @param[in] callback
629  *  A function to call on DMA completion, use NULL if not needed.
630  *
631  * @param[in] cbUserParam
632  *  An optional user parameter to feed to the callback function. Use NULL if
633  *  not needed.
634  *
635  * @return
636  *   @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
637  *   DMADRV @ref Ecode_t is returned.
638  ******************************************************************************/
DMADRV_PeripheralMemory(unsigned int channelId,DMADRV_PeripheralSignal_t peripheralSignal,void * dst,void * src,bool dstInc,int len,DMADRV_DataSize_t size,DMADRV_Callback_t callback,void * cbUserParam)639 Ecode_t DMADRV_PeripheralMemory(unsigned int          channelId,
640                                 DMADRV_PeripheralSignal_t
641                                 peripheralSignal,
642                                 void                  *dst,
643                                 void                  *src,
644                                 bool                  dstInc,
645                                 int                   len,
646                                 DMADRV_DataSize_t     size,
647                                 DMADRV_Callback_t     callback,
648                                 void                  *cbUserParam)
649 {
650   return StartTransfer(dmaModeBasic,
651                        dmaDirectionPeripheralToMem,
652                        channelId,
653                        peripheralSignal,
654                        dst,
655                        src,
656                        NULL,
657                        dstInc,
658                        len,
659                        size,
660                        callback,
661                        cbUserParam);
662 }
663 
664 /***************************************************************************//**
665  * @brief
666  *  Start a peripheral to memory ping-pong DMA transfer.
667  *
668  * @param[in] channelId
669  *  The channel ID to use for the transfer.
670  *
671  * @param[in] peripheralSignal
672  *  Selects which peripheral/peripheralsignal to use.
673  *
674  * @param[in] dst0
675  *  A destination memory address of the first (ping) buffer.
676  *
677  * @param[in] dst1
678  *  A destination memory address of the second (pong) buffer.
679  *
680  * @param[in] src
681  *  A source memory (peripheral register) address.
682  *
683  * @param[in] dstInc
684  *  Set to true to enable destination address increment (increments according
685  *  to @a size parameter).
686  *
687  * @param[in] len
688  *  A number of items (of @a size size) to transfer.
689  *
690  * @param[in] size
691  *  An item size, byte, halfword or word.
692  *
693  * @param[in] callback
694  *  A function to call on DMA completion, use NULL if not needed.
695  *
696  * @param[in] cbUserParam
697  *  An optional user parameter to feed to the callback function. Use NULL if
698  *  not needed.
699  *
700  * @return
701  *   @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
702  *   DMADRV @ref Ecode_t is returned.
703  ******************************************************************************/
DMADRV_PeripheralMemoryPingPong(unsigned int channelId,DMADRV_PeripheralSignal_t peripheralSignal,void * dst0,void * dst1,void * src,bool dstInc,int len,DMADRV_DataSize_t size,DMADRV_Callback_t callback,void * cbUserParam)704 Ecode_t DMADRV_PeripheralMemoryPingPong(
705   unsigned int          channelId,
706   DMADRV_PeripheralSignal_t
707   peripheralSignal,
708   void                  *dst0,
709   void                  *dst1,
710   void                  *src,
711   bool                  dstInc,
712   int                   len,
713   DMADRV_DataSize_t     size,
714   DMADRV_Callback_t     callback,
715   void                  *cbUserParam)
716 {
717   return StartTransfer(dmaModePingPong,
718                        dmaDirectionPeripheralToMem,
719                        channelId,
720                        peripheralSignal,
721                        dst0,
722                        dst1,
723                        src,
724                        dstInc,
725                        len,
726                        size,
727                        callback,
728                        cbUserParam);
729 }
730 
731 /***************************************************************************//**
732  * @brief
733  *  Pause an ongoing DMA transfer.
734  *
735  * @param[in] channelId
736  *  The channel ID of the transfer to pause.
737  *
738  * @return
739  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
740  *  DMADRV @ref Ecode_t is returned.
741  ******************************************************************************/
DMADRV_PauseTransfer(unsigned int channelId)742 Ecode_t DMADRV_PauseTransfer(unsigned int channelId)
743 {
744   if ( !initialized ) {
745     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
746   }
747 
748   if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) {
749     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
750   }
751 
752   if ( chTable[channelId].allocated == false ) {
753     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
754   }
755 
756 #if defined(EMDRV_DMADRV_UDMA)
757   DMA_ChannelRequestEnable(channelId, false);
758 #elif defined(EMDRV_DMADRV_LDMA)
759   LDMA_EnableChannelRequest(channelId, false);
760 #elif defined(EMDRV_DMADRV_LDMA_S3)
761   sl_hal_ldma_disable_channel_request(LDMA0, channelId);
762 #endif
763 
764   return ECODE_EMDRV_DMADRV_OK;
765 }
766 
767 /***************************************************************************//**
768  * @brief
769  *  Resume an ongoing DMA transfer.
770  *
771  * @param[in] channelId
772  *  The channel ID of the transfer to resume.
773  *
774  * @return
775  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
776  *  DMADRV @ref Ecode_t is returned.
777  ******************************************************************************/
DMADRV_ResumeTransfer(unsigned int channelId)778 Ecode_t DMADRV_ResumeTransfer(unsigned int channelId)
779 {
780   if ( !initialized ) {
781     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
782   }
783 
784   if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) {
785     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
786   }
787 
788   if ( chTable[channelId].allocated == false ) {
789     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
790   }
791 
792 #if defined(EMDRV_DMADRV_UDMA)
793   DMA_ChannelRequestEnable(channelId, true);
794 #elif defined(EMDRV_DMADRV_LDMA)
795   LDMA_EnableChannelRequest(channelId, true);
796 #elif defined(EMDRV_DMADRV_LDMA_S3)
797   sl_hal_ldma_enable_channel_request(LDMA0, channelId);
798 #endif
799 
800   return ECODE_EMDRV_DMADRV_OK;
801 }
802 
803 /***************************************************************************//**
804  * @brief
805  *  Stop an ongoing DMA transfer.
806  *
807  * @param[in] channelId
808  *  The channel ID of the transfer to stop.
809  *
810  * @return
811  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
812  *  DMADRV @ref Ecode_t is returned.
813  ******************************************************************************/
DMADRV_StopTransfer(unsigned int channelId)814 Ecode_t DMADRV_StopTransfer(unsigned int channelId)
815 {
816   if ( !initialized ) {
817     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
818   }
819 
820   if ( channelId >= EMDRV_DMADRV_DMA_CH_COUNT ) {
821     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
822   }
823 
824   if ( chTable[channelId].allocated == false ) {
825     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
826   }
827 
828 #if defined(EMDRV_DMADRV_UDMA)
829   DMA_ChannelEnable(channelId, false);
830 #elif defined(EMDRV_DMADRV_LDMA)
831   LDMA_StopTransfer(channelId);
832 #elif defined(EMDRV_DMADRV_LDMA_S3)
833   sl_hal_ldma_stop_transfer(LDMA0, channelId);
834 #endif
835 
836   return ECODE_EMDRV_DMADRV_OK;
837 }
838 
839 /***************************************************************************//**
840  * @brief
841  *  Check if a transfer is running.
842  *
843  * @param[in] channelId
844  *  The channel ID of the transfer to check.
845  *
846  * @param[out] active
847  *  True if transfer is running, false otherwise.
848  *
849  * @return
850  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
851  *  DMADRV @ref Ecode_t is returned.
852  ******************************************************************************/
DMADRV_TransferActive(unsigned int channelId,bool * active)853 Ecode_t DMADRV_TransferActive(unsigned int channelId, bool *active)
854 {
855   if ( !initialized ) {
856     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
857   }
858 
859   if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT)
860        || (active == NULL) ) {
861     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
862   }
863 
864   if ( chTable[channelId].allocated == false ) {
865     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
866   }
867 
868 #if defined(EMDRV_DMADRV_UDMA)
869   if ( DMA_ChannelEnabled(channelId) )
870 #elif defined(EMDRV_DMADRV_LDMA)
871   if ( LDMA_ChannelEnabled(channelId) )
872 #elif defined(EMDRV_DMADRV_LDMA_S3)
873   if ( sl_hal_ldma_channel_is_enabled(LDMA0, channelId) )
874 #endif
875   {
876     *active = true;
877   } else {
878     *active = false;
879   }
880 
881   return ECODE_EMDRV_DMADRV_OK;
882 }
883 
884 /***************************************************************************//**
885  * @brief
886  *  Check if a transfer complete is pending.
887  *
888  * @details
889  *  Will check the channel interrupt flag. This assumes that the DMA is configured
890  *  to give a completion interrupt.
891  *
892  * @param[in] channelId
893  *  The channel ID of the transfer to check.
894  *
895  * @param[out] pending
896  *  True if a transfer complete is pending, false otherwise.
897  *
898  * @return
899  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
900  *  DMADRV @ref Ecode_t is returned.
901  ******************************************************************************/
DMADRV_TransferCompletePending(unsigned int channelId,bool * pending)902 Ecode_t DMADRV_TransferCompletePending(unsigned int channelId, bool *pending)
903 {
904   if ( !initialized ) {
905     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
906   }
907 
908   if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT)
909        || (pending == NULL) ) {
910     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
911   }
912 
913   if ( chTable[channelId].allocated == false ) {
914     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
915   }
916 
917 #if defined(EMDRV_DMADRV_UDMA)
918   if ( DMA->IF & (1 << channelId) )
919 #elif defined(EMDRV_DMADRV_LDMA)
920   if ( LDMA->IF & (1 << channelId) )
921 #elif defined(EMDRV_DMADRV_LDMA_S3)
922   if ( sl_hal_ldma_get_pending_interrupts(LDMA0) & (1 << channelId) )
923 #endif
924   {
925     *pending = true;
926   } else {
927     *pending = false;
928   }
929 
930   return ECODE_EMDRV_DMADRV_OK;
931 }
932 
933 /***************************************************************************//**
934  * @brief
935  *  Check if a transfer has completed.
936  *
937  * @note
938  *  This function should be used in a polled environment.
939  *  Will only work reliably for transfers NOT using the completion interrupt.
940  *  On UDMA, it will only work on basic transfers on the primary channel.
941  *
942  * @param[in] channelId
943  *  The channel ID of the transfer to check.
944  *
945  * @param[out] done
946  *  True if a transfer has completed, false otherwise.
947  *
948  * @return
949  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
950  *  DMADRV @ref Ecode_t is returned.
951  ******************************************************************************/
DMADRV_TransferDone(unsigned int channelId,bool * done)952 Ecode_t DMADRV_TransferDone(unsigned int channelId, bool *done)
953 {
954 #if defined(EMDRV_DMADRV_UDMA)
955   uint32_t remaining, iflag;
956 #endif
957 
958   if ( !initialized ) {
959     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
960   }
961 
962   if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT)
963        || (done == NULL) ) {
964     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
965   }
966 
967   if ( chTable[channelId].allocated == false ) {
968     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
969   }
970 
971 #if defined(EMDRV_DMADRV_UDMA)
972   CORE_ATOMIC_SECTION(
973     /* This works for primary channel only ! */
974     remaining = (dmaControlBlock[channelId].CTRL
975                  & _DMA_CTRL_N_MINUS_1_MASK)
976                 >> _DMA_CTRL_N_MINUS_1_SHIFT;
977     iflag = DMA->IF;
978     )
979 
980   if ( (remaining == 0) && (iflag & (1 << channelId)) ) {
981     *done = true;
982   } else {
983     *done = false;
984   }
985 #elif defined(EMDRV_DMADRV_LDMA)
986   *done = LDMA_TransferDone(channelId);
987 #elif defined(EMDRV_DMADRV_LDMA_S3)
988   *done = sl_hal_ldma_transfer_is_done(LDMA0, channelId);
989 #endif
990 
991   return ECODE_EMDRV_DMADRV_OK;
992 }
993 
994 /***************************************************************************//**
995  * @brief
996  *  Get number of items remaining in a transfer.
997  *
998  * @note
999  *  This function does not take into account that a DMA transfer with
1000  *  a chain of linked transfers might be ongoing. It will only check the
1001  *  count for the current transfer.
1002  *  On UDMA, it will only work on the primary channel.
1003  *
1004  * @param[in] channelId
1005  *  The channel ID of the transfer to check.
1006  *
1007  * @param[out] remaining
1008  *  A number of items remaining in the transfer.
1009  *
1010  * @return
1011  *  @ref ECODE_EMDRV_DMADRV_OK on success. On failure, an appropriate
1012  *  DMADRV @ref Ecode_t is returned.
1013  ******************************************************************************/
DMADRV_TransferRemainingCount(unsigned int channelId,int * remaining)1014 Ecode_t DMADRV_TransferRemainingCount(unsigned int channelId,
1015                                       int *remaining)
1016 {
1017 #if defined(EMDRV_DMADRV_UDMA)
1018   uint32_t remain, iflag;
1019 #endif
1020 
1021   if ( !initialized ) {
1022     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
1023   }
1024 
1025   if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT)
1026        || (remaining == NULL) ) {
1027     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
1028   }
1029 
1030   if ( chTable[channelId].allocated == false ) {
1031     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
1032   }
1033 
1034 #if defined(EMDRV_DMADRV_UDMA)
1035   CORE_ATOMIC_SECTION(
1036     /* This works for the primary channel only ! */
1037     remain = (dmaControlBlock[channelId].CTRL
1038               & _DMA_CTRL_N_MINUS_1_MASK)
1039              >> _DMA_CTRL_N_MINUS_1_SHIFT;
1040     iflag = DMA->IF;
1041     )
1042 
1043   if ( (remain == 0) && (iflag & (1 << channelId)) ) {
1044     *remaining = 0;
1045   } else {
1046     *remaining = 1 + remain;
1047   }
1048 #elif defined(EMDRV_DMADRV_LDMA)
1049   *remaining = LDMA_TransferRemainingCount(channelId);
1050 #elif defined(EMDRV_DMADRV_LDMA_S3)
1051   *remaining = sl_hal_ldma_transfer_remaining_count(LDMA0, channelId);
1052 #endif
1053 
1054   return ECODE_EMDRV_DMADRV_OK;
1055 }
1056 
1057 /// @cond DO_NOT_INCLUDE_WITH_DOXYGEN
1058 
1059 #if defined(EMDRV_DMADRV_LDMA)
1060 /***************************************************************************//**
1061  * @brief
1062  *  An interrupt handler for LDMA.
1063  ******************************************************************************/
LDMA_IRQHandler(void)1064 void LDMA_IRQHandler(void)
1065 {
1066   bool stop;
1067   ChTable_t *ch;
1068   uint32_t pending, chnum, chmask;
1069 
1070   /* Get all pending and enabled interrupts. */
1071   pending  = LDMA->IF;
1072   pending &= LDMA->IEN;
1073 
1074   /* Check for LDMA error. */
1075   if ( pending & LDMA_IF_ERROR ) {
1076     /* Loop to enable debugger to see what has happened. */
1077     while (true) {
1078       /* Wait forever. */
1079     }
1080   }
1081 
1082   /* Iterate over all LDMA channels. */
1083   for ( chnum = 0, chmask = 1;
1084         chnum < EMDRV_DMADRV_DMA_CH_COUNT;
1085         chnum++, chmask <<= 1 ) {
1086     if ( pending & chmask ) {
1087       /* Clear the interrupt flag. */
1088 #if defined (LDMA_HAS_SET_CLEAR)
1089       LDMA->IF_CLR = chmask;
1090 #else
1091       LDMA->IFC = chmask;
1092 #endif
1093 
1094       ch = &chTable[chnum];
1095       if ( ch->callback != NULL ) {
1096         ch->callbackCount++;
1097         stop = !ch->callback(chnum, ch->callbackCount, ch->userParam);
1098 
1099         if ( (ch->mode == dmaModePingPong) && stop ) {
1100           dmaXfer[chnum].desc[0].xfer.link = 0;
1101           dmaXfer[chnum].desc[1].xfer.link = 0;
1102         }
1103       }
1104     }
1105   }
1106 }
1107 #endif /* defined( EMDRV_DMADRV_LDMA ) */
1108 
1109 #if defined(EMDRV_DMADRV_LDMA_S3)
1110 /***************************************************************************//**
1111  * @brief
1112  *  Default interrupt handler for LDMA common to all interrupt channel lines.
1113  *
1114  * @param[in] chnum
1115  *  The channel ID responsible for the interrupt signal trigger.
1116  ******************************************************************************/
LDMA_IRQHandlerDefault(uint8_t chnum)1117 static void LDMA_IRQHandlerDefault(uint8_t chnum)
1118 {
1119   bool stop;
1120   ChTable_t *ch;
1121   uint32_t pending;
1122   uint32_t chmask;
1123 
1124   /* Get all pending and enabled interrupts. */
1125   pending = sl_hal_ldma_get_enabled_pending_interrupts(LDMA0);
1126 
1127   /* Check for LDMA error. */
1128   if ( pending & (LDMA_IF_ERROR0 << chnum) ) {
1129     /* Loop to enable debugger to see what has happened. */
1130     while (true) {
1131       /* Wait forever. */
1132     }
1133   }
1134 
1135   chmask = 1 << chnum;
1136   if ( pending & chmask ) {
1137     /* Clear the interrupt flag. */
1138     sl_hal_ldma_clear_interrupts(LDMA0, chmask);
1139 
1140     /* Callback called if it was provided for the given channel. */
1141     ch = &chTable[chnum];
1142     if ( ch->callback != NULL ) {
1143       ch->callbackCount++;
1144       stop = !ch->callback(chnum, ch->callbackCount, ch->userParam);
1145 
1146       /* Continue or not a ping-pong transfer. */
1147       if ( (ch->mode == dmaModePingPong) && stop ) {
1148         dmaXfer[chnum].desc[0].xfer.link = 0;
1149         dmaXfer[chnum].desc[1].xfer.link = 0;
1150       }
1151     }
1152   }
1153 }
1154 
1155 /***************************************************************************//**
1156  * @brief
1157  *  Root interrupt handler for LDMA channel 0.
1158  ******************************************************************************/
LDMA0_CHNL0_IRQHandler(void)1159 void LDMA0_CHNL0_IRQHandler(void)
1160 {
1161   LDMA_IRQHandlerDefault(0);
1162 }
1163 
1164 /***************************************************************************//**
1165  * @brief
1166  *  Root interrupt handler for LDMA channel 1.
1167  ******************************************************************************/
LDMA0_CHNL1_IRQHandler(void)1168 void LDMA0_CHNL1_IRQHandler(void)
1169 {
1170   LDMA_IRQHandlerDefault(1);
1171 }
1172 
1173 /***************************************************************************//**
1174  * @brief
1175  *  Root interrupt handler for LDMA channel 2.
1176  ******************************************************************************/
LDMA0_CHNL2_IRQHandler(void)1177 void LDMA0_CHNL2_IRQHandler(void)
1178 {
1179   LDMA_IRQHandlerDefault(2);
1180 }
1181 
1182 /***************************************************************************//**
1183  * @brief
1184  *  Root interrupt handler for LDMA channel 3.
1185  ******************************************************************************/
LDMA0_CHNL3_IRQHandler(void)1186 void LDMA0_CHNL3_IRQHandler(void)
1187 {
1188   LDMA_IRQHandlerDefault(3);
1189 }
1190 
1191 /***************************************************************************//**
1192  * @brief
1193  *  Root interrupt handler for LDMA channel 4.
1194  ******************************************************************************/
LDMA0_CHNL4_IRQHandler(void)1195 void LDMA0_CHNL4_IRQHandler(void)
1196 {
1197   LDMA_IRQHandlerDefault(4);
1198 }
1199 
1200 /***************************************************************************//**
1201  * @brief
1202  *  Root interrupt handler for LDMA channel 5.
1203  ******************************************************************************/
LDMA0_CHNL5_IRQHandler(void)1204 void LDMA0_CHNL5_IRQHandler(void)
1205 {
1206   LDMA_IRQHandlerDefault(5);
1207 }
1208 
1209 /***************************************************************************//**
1210  * @brief
1211  *  Root interrupt handler for LDMA channel 6.
1212  ******************************************************************************/
LDMA0_CHNL6_IRQHandler(void)1213 void LDMA0_CHNL6_IRQHandler(void)
1214 {
1215   LDMA_IRQHandlerDefault(6);
1216 }
1217 
1218 /***************************************************************************//**
1219  * @brief
1220  *  Root interrupt handler for LDMA channel 7.
1221  ******************************************************************************/
LDMA0_CHNL7_IRQHandler(void)1222 void LDMA0_CHNL7_IRQHandler(void)
1223 {
1224   LDMA_IRQHandlerDefault(7);
1225 }
1226 
1227 #endif /* defined( EMDRV_DMADRV_LDMA_S3 ) */
1228 
1229 #if defined(EMDRV_DMADRV_UDMA)
1230 /***************************************************************************//**
1231  * @brief
1232  *  A callback function for UDMA basic transfers.
1233  ******************************************************************************/
DmaBasicCallback(unsigned int channel,bool primary,void * user)1234 static void DmaBasicCallback(unsigned int channel, bool primary, void *user)
1235 {
1236   ChTable_t *ch = &chTable[channel];
1237   (void)user;
1238   (void)primary;
1239 
1240   if ( ch->callback != NULL ) {
1241     ch->callbackCount++;
1242     ch->callback(channel, ch->callbackCount, ch->userParam);
1243   }
1244 }
1245 #endif
1246 
1247 #if defined(EMDRV_DMADRV_UDMA)
1248 /***************************************************************************//**
1249  * @brief
1250  *  A callback function for UDMA ping-pong transfers.
1251  ******************************************************************************/
DmaPingPongCallback(unsigned int channel,bool primary,void * user)1252 static void DmaPingPongCallback(unsigned int channel, bool primary, void *user)
1253 {
1254   bool stop = true;
1255   ChTable_t *ch = &chTable[channel];
1256 
1257   (void)user;
1258 
1259   if ( ch->callback != NULL ) {
1260     ch->callbackCount++;
1261     stop = !ch->callback(channel, ch->callbackCount, ch->userParam);
1262   }
1263 
1264   DMA_RefreshPingPong(channel,
1265                       primary,
1266                       false,
1267                       NULL,
1268                       NULL,
1269                       ch->length - 1,
1270                       stop);
1271 }
1272 #endif
1273 
1274 #if defined(EMDRV_DMADRV_UDMA)
1275 /***************************************************************************//**
1276  * @brief
1277  *  Start a UDMA transfer.
1278  ******************************************************************************/
StartTransfer(DmaMode_t mode,DmaDirection_t direction,unsigned int channelId,DMADRV_PeripheralSignal_t peripheralSignal,void * buf0,void * buf1,void * buf2,bool bufInc,int len,DMADRV_DataSize_t size,DMADRV_Callback_t callback,void * cbUserParam)1279 static Ecode_t StartTransfer(DmaMode_t             mode,
1280                              DmaDirection_t        direction,
1281                              unsigned int          channelId,
1282                              DMADRV_PeripheralSignal_t
1283                              peripheralSignal,
1284                              void                  *buf0,
1285                              void                  *buf1,
1286                              void                  *buf2,
1287                              bool                  bufInc,
1288                              int                   len,
1289                              DMADRV_DataSize_t     size,
1290                              DMADRV_Callback_t     callback,
1291                              void                  *cbUserParam)
1292 {
1293   ChTable_t *ch;
1294   DMA_CfgChannel_TypeDef chCfg;
1295   DMA_CfgDescr_TypeDef   descrCfg;
1296 
1297   if ( !initialized ) {
1298     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
1299   }
1300 
1301   if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT)
1302        || (buf0 == NULL)
1303        || (buf1 == NULL)
1304        || (len > DMADRV_MAX_XFER_COUNT)
1305        || ((mode == dmaModePingPong) && (buf2 == NULL)) ) {
1306     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
1307   }
1308 
1309   ch = &chTable[channelId];
1310   if ( ch->allocated == false ) {
1311     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
1312   }
1313 
1314   /* Se tup the interrupt callback routine. */
1315   if ( mode == dmaModeBasic ) {
1316     dmaCallBack[channelId].cbFunc  = DmaBasicCallback;
1317   } else {
1318     dmaCallBack[channelId].cbFunc  = DmaPingPongCallback;
1319   }
1320   dmaCallBack[channelId].userPtr = NULL;
1321 
1322   /* Set up the channel */
1323   chCfg.highPri = false;              /* Can't use hi pri with peripherals. */
1324 
1325   /* Whether the interrupt is needed. */
1326   if ( (callback != NULL) || (mode == dmaModePingPong) ) {
1327     chCfg.enableInt = true;
1328   } else {
1329     chCfg.enableInt = false;
1330   }
1331   chCfg.select = peripheralSignal;
1332   chCfg.cb     = &dmaCallBack[channelId];
1333   DMA_CfgChannel(channelId, &chCfg);
1334 
1335   /* Set up the channel descriptor. */
1336   if ( direction == dmaDirectionMemToPeripheral ) {
1337     if ( bufInc ) {
1338       if ( size == dmadrvDataSize1 ) {
1339         descrCfg.srcInc = dmaDataInc1;
1340       } else if ( size == dmadrvDataSize2 ) {
1341         descrCfg.srcInc = dmaDataInc2;
1342       } else { /* dmadrvDataSize4 */
1343         descrCfg.srcInc = dmaDataInc4;
1344       }
1345     } else {
1346       descrCfg.srcInc = dmaDataIncNone;
1347     }
1348     descrCfg.dstInc = dmaDataIncNone;
1349   } else {
1350     if ( bufInc ) {
1351       if ( size == dmadrvDataSize1 ) {
1352         descrCfg.dstInc = dmaDataInc1;
1353       } else if ( size == dmadrvDataSize2 ) {
1354         descrCfg.dstInc = dmaDataInc2;
1355       } else { /* dmadrvDataSize4 */
1356         descrCfg.dstInc = dmaDataInc4;
1357       }
1358     } else {
1359       descrCfg.dstInc = dmaDataIncNone;
1360     }
1361     descrCfg.srcInc = dmaDataIncNone;
1362   }
1363   descrCfg.size    = (DMA_DataSize_TypeDef)size;
1364   descrCfg.arbRate = dmaArbitrate1;
1365   descrCfg.hprot   = 0;
1366   DMA_CfgDescr(channelId, true, &descrCfg);
1367   if ( mode == dmaModePingPong ) {
1368     DMA_CfgDescr(channelId, false, &descrCfg);
1369   }
1370 
1371   ch->callback      = callback;
1372   ch->userParam     = cbUserParam;
1373   ch->callbackCount = 0;
1374   ch->length        = len;
1375 
1376   DMA->IFC = 1 << channelId;
1377 
1378   /* Start the DMA cycle. */
1379   if ( mode == dmaModeBasic ) {
1380     DMA_ActivateBasic(channelId, true, false, buf0, buf1, len - 1);
1381   } else {
1382     if ( direction == dmaDirectionMemToPeripheral ) {
1383       DMA_ActivatePingPong(channelId,
1384                            false,
1385                            buf0,                              /* dest */
1386                            buf1,                              /* src  */
1387                            len - 1,
1388                            buf0,                              /* dest */
1389                            buf2,                              /* src  */
1390                            len - 1);
1391     } else {
1392       DMA_ActivatePingPong(channelId,
1393                            false,
1394                            buf0,                              /* dest */
1395                            buf2,                              /* src  */
1396                            len - 1,
1397                            buf1,                              /* dest */
1398                            buf2,                              /* src  */
1399                            len - 1);
1400     }
1401   }
1402 
1403   return ECODE_EMDRV_DMADRV_OK;
1404 }
1405 #endif /* defined( EMDRV_DMADRV_UDMA ) */
1406 
1407 #if defined(EMDRV_DMADRV_LDMA)
1408 /***************************************************************************//**
1409  * @brief
1410  *  Start an LDMA transfer.
1411  ******************************************************************************/
StartTransfer(DmaMode_t mode,DmaDirection_t direction,unsigned int channelId,DMADRV_PeripheralSignal_t peripheralSignal,void * buf0,void * buf1,void * buf2,bool bufInc,int len,DMADRV_DataSize_t size,DMADRV_Callback_t callback,void * cbUserParam)1412 static Ecode_t StartTransfer(DmaMode_t             mode,
1413                              DmaDirection_t        direction,
1414                              unsigned int          channelId,
1415                              DMADRV_PeripheralSignal_t
1416                              peripheralSignal,
1417                              void                  *buf0,
1418                              void                  *buf1,
1419                              void                  *buf2,
1420                              bool                  bufInc,
1421                              int                   len,
1422                              DMADRV_DataSize_t     size,
1423                              DMADRV_Callback_t     callback,
1424                              void                  *cbUserParam)
1425 {
1426   ChTable_t *ch;
1427   LDMA_TransferCfg_t xfer;
1428   LDMA_Descriptor_t *desc;
1429 
1430   if ( !initialized ) {
1431     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
1432   }
1433 
1434   if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT)
1435        || (buf0 == NULL)
1436        || (buf1 == NULL)
1437        || (len > DMADRV_MAX_XFER_COUNT)
1438        || ((mode == dmaModePingPong) && (buf2 == NULL)) ) {
1439     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
1440   }
1441 
1442   ch = &chTable[channelId];
1443   if ( ch->allocated == false ) {
1444     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
1445   }
1446 
1447   xfer = xferCfgPeripheral;
1448   desc = &dmaXfer[channelId].desc[0];
1449 
1450   if ( direction == dmaDirectionMemToPeripheral ) {
1451     *desc = m2p;
1452     if ( !bufInc ) {
1453       desc->xfer.srcInc = ldmaCtrlSrcIncNone;
1454     }
1455   } else {
1456     *desc = p2m;
1457     if ( !bufInc ) {
1458       desc->xfer.dstInc = ldmaCtrlDstIncNone;
1459     }
1460   }
1461 
1462   xfer.ldmaReqSel    = peripheralSignal;
1463   desc->xfer.xferCnt = len - 1;
1464   desc->xfer.dstAddr = (uint32_t)(uint8_t *)buf0;
1465   desc->xfer.srcAddr = (uint32_t)(uint8_t *)buf1;
1466   desc->xfer.size    = size;
1467 
1468   if ( mode == dmaModePingPong ) {
1469     desc->xfer.linkMode = ldmaLinkModeRel;
1470     desc->xfer.link     = 1;
1471     desc->xfer.linkAddr = 4;      /* Refer to the "pong" descriptor. */
1472 
1473     /* Set the "pong" descriptor equal to the "ping" descriptor. */
1474     dmaXfer[channelId].desc[1] = *desc;
1475     /* Refer to the "ping" descriptor. */
1476     dmaXfer[channelId].desc[1].xfer.linkAddr = -4;
1477     dmaXfer[channelId].desc[1].xfer.srcAddr = (uint32_t)(uint8_t *)buf2;
1478 
1479     if ( direction == dmaDirectionPeripheralToMem ) {
1480       dmaXfer[channelId].desc[1].xfer.dstAddr = (uint32_t)(uint8_t *)buf1;
1481       desc->xfer.srcAddr = (uint32_t)(uint8_t *)buf2;
1482     }
1483   }
1484 
1485   /* Whether an interrupt is needed. */
1486   if ( (callback == NULL) && (mode == dmaModeBasic) ) {
1487     desc->xfer.doneIfs = 0;
1488   }
1489 
1490   ch->callback      = callback;
1491   ch->userParam     = cbUserParam;
1492   ch->callbackCount = 0;
1493   ch->mode          = mode;
1494 
1495   LDMA_StartTransfer(channelId, &xfer, desc);
1496 
1497   return ECODE_EMDRV_DMADRV_OK;
1498 }
1499 #endif /* defined( EMDRV_DMADRV_LDMA ) */
1500 
1501 #if defined(EMDRV_DMADRV_LDMA_S3)
1502 /***************************************************************************//**
1503  * @brief
1504  *  Start an LDMA transfer.
1505  ******************************************************************************/
StartTransfer(DmaMode_t mode,DmaDirection_t direction,unsigned int channelId,DMADRV_PeripheralSignal_t peripheralSignal,void * buf0,void * buf1,void * buf2,bool bufInc,int len,DMADRV_DataSize_t size,DMADRV_Callback_t callback,void * cbUserParam)1506 static Ecode_t StartTransfer(DmaMode_t             mode,
1507                              DmaDirection_t        direction,
1508                              unsigned int          channelId,
1509                              DMADRV_PeripheralSignal_t
1510                              peripheralSignal,
1511                              void                  *buf0,
1512                              void                  *buf1,
1513                              void                  *buf2,
1514                              bool                  bufInc,
1515                              int                   len,
1516                              DMADRV_DataSize_t     size,
1517                              DMADRV_Callback_t     callback,
1518                              void                  *cbUserParam)
1519 {
1520   ChTable_t *ch;
1521   sl_hal_ldma_transfer_config_t xfer;
1522   sl_hal_ldma_descriptor_t *desc;
1523 
1524   if ( !initialized ) {
1525     return ECODE_EMDRV_DMADRV_NOT_INITIALIZED;
1526   }
1527 
1528   if ( (channelId >= EMDRV_DMADRV_DMA_CH_COUNT)
1529        || (buf0 == NULL)
1530        || (buf1 == NULL)
1531        || (len > DMADRV_MAX_XFER_COUNT)
1532        || ((mode == dmaModePingPong) && (buf2 == NULL)) ) {
1533     return ECODE_EMDRV_DMADRV_PARAM_ERROR;
1534   }
1535 
1536   ch = &chTable[channelId];
1537   if ( ch->allocated == false ) {
1538     return ECODE_EMDRV_DMADRV_CH_NOT_ALLOCATED;
1539   }
1540 
1541   xfer = xferCfgPeripheral;
1542   desc = &dmaXfer[channelId].desc[0];
1543 
1544   if ( direction == dmaDirectionMemToPeripheral ) {
1545     *desc = m2p;
1546     if ( !bufInc ) {
1547       desc->xfer.src_inc = SL_HAL_LDMA_CTRL_SRC_INC_NONE;
1548     }
1549   } else {
1550     *desc = p2m;
1551     if ( !bufInc ) {
1552       desc->xfer.dst_inc = SL_HAL_LDMA_CTRL_DST_INC_NONE;
1553     }
1554   }
1555 
1556   xfer.request_sel      = peripheralSignal;
1557   desc->xfer.xfer_count = len - 1;
1558   desc->xfer.dst_addr   = (uint32_t)(uint8_t *)buf0;
1559   desc->xfer.src_addr   = (uint32_t)(uint8_t *)buf1;
1560   desc->xfer.size       = size;
1561 
1562   if ( mode == dmaModePingPong ) {
1563     desc->xfer.link_mode = SL_HAL_LDMA_LINK_MODE_REL;
1564     desc->xfer.link      = 1;
1565     desc->xfer.link_addr = 4;      /* Refer to the "pong" descriptor. */
1566 
1567     /* Set the "pong" descriptor equal to the "ping" descriptor. */
1568     dmaXfer[channelId].desc[1] = *desc;
1569     /* Refer to the "ping" descriptor. */
1570     dmaXfer[channelId].desc[1].xfer.link_addr = -4;
1571     dmaXfer[channelId].desc[1].xfer.src_addr = (uint32_t)(uint8_t *)buf2;
1572 
1573     if ( direction == dmaDirectionPeripheralToMem ) {
1574       dmaXfer[channelId].desc[1].xfer.dst_addr = (uint32_t)(uint8_t *)buf1;
1575       desc->xfer.src_addr = (uint32_t)(uint8_t *)buf2;
1576     }
1577   }
1578 
1579   /* Whether an interrupt is needed. */
1580   if ( (callback == NULL) && (mode == dmaModeBasic) ) {
1581     desc->xfer.done_ifs = 0;
1582   }
1583 
1584   ch->callback      = callback;
1585   ch->userParam     = cbUserParam;
1586   ch->callbackCount = 0;
1587   ch->mode          = mode;
1588 
1589   sl_hal_ldma_init_transfer(LDMA0, channelId, &xfer, desc);
1590   sl_hal_ldma_start_transfer(LDMA0, channelId);
1591   sl_hal_ldma_enable_interrupts(LDMA0, (0x1UL << channelId));
1592 
1593   return ECODE_EMDRV_DMADRV_OK;
1594 }
1595 #endif /* defined( EMDRV_DMADRV_LDMA_S3 ) */
1596 
1597 /// @endcond
1598 
1599 // ******** THE REST OF THE FILE IS DOCUMENTATION ONLY !***********************
1600 /// @addtogroup dmadrv DMADRV - DMA Driver
1601 /// @brief Direct Memory Access Driver
1602 /// @{
1603 ///
1604 ///   @details
1605 ///
1606 ///
1607 ///   @n @section dmadrv_intro Introduction
1608 ///
1609 ///   The DMADRV driver supports writing code using DMA which will work
1610 ///   regardless of the type of the DMA controller on the underlying microcontroller.
1611 ///   Additionally, DMA can be used in several modules that are
1612 ///   completely unaware of each other.
1613 ///   The driver does not preclude use of the native emlib or peripheral API of the
1614 ///   underlying DMA controller. On the contrary, it will often result in more efficient
1615 ///   code and is necessary for complex DMA operations. The housekeeping
1616 ///   functions of this driver are valuable even in this use-case.
1617 ///
1618 ///   The dmadrv.c and dmadrv.h source files are in the
1619 ///   emdrv/dmadrv folder.
1620 ///
1621 ///   @note DMA transfer completion callback functions are called from within the
1622 ///   DMA interrupt handler. On versions of the DMA controller with one interrupt per
1623 ///   channel, the callback function is called from its respective channel interrupt
1624 ///   handler.
1625 ///
1626 ///   @n @section dmadrv_conf Configuration Options
1627 ///
1628 ///   Some properties of the DMADRV driver are compile-time configurable. These
1629 ///   properties are stored in a file named dmadrv_config.h. A template for this
1630 ///   file, containing default values, is in the emdrv/config folder. IC specific
1631 ///   versions of dmadrv_config.h files are available in config/sx_xch directories.
1632 ///   Currently the configuration options are as follows:
1633 ///   @li The interrupt priority of the DMA peripheral.
1634 ///   @li A number of DMA channels to support.
1635 ///   @li Use the native emlib/peripheral API belonging to the underlying DMA hardware in
1636 ///      combination with the DMADRV API.
1637 ///
1638 ///   Both configuration options will help reduce the driver's RAM footprint.
1639 ///
1640 ///   To configure DMADRV, provide a custom configuration file. This is an
1641 ///   example dmadrv_config.h file:
1642 ///   @code{.c}
1643 /// #ifndef __SILICON_LABS_DMADRV_CONFIG_H__
1644 /// #define __SILICON_LABS_DMADRV_CONFIG_H__
1645 ///
1646 ///   // DMADRV DMA interrupt priority configuration option.
1647 ///   // Set DMA interrupt priority. Range is 0..7, 0 is the highest priority.
1648 /// #define EMDRV_DMADRV_DMA_IRQ_PRIORITY 4
1649 ///
1650 ///   // DMADRV channel count configuration option.
1651 ///   // A number of DMA channels to support. A lower DMA channel count will reduce
1652 ///   // RAM footprint.
1653 /// #define EMDRV_DMADRV_DMA_CH_COUNT 4
1654 ///
1655 /// #endif
1656 ///   @endcode
1657 ///
1658 ///   @n @section dmadrv_api The API
1659 ///
1660 ///   This section contains brief descriptions of the API functions.
1661 ///   For more information about input and output parameters and return values,
1662 ///   click on the hyperlinked function names. Most functions return an error
1663 ///   code, @ref ECODE_EMDRV_DMADRV_OK is returned on success,
1664 ///   see @ref ecode and @ref dmadrv_error_codes for other error codes.
1665 ///
1666 ///   The application code must include @em dmadrv.h header file.
1667 ///
1668 ///   @ref DMADRV_Init(), @ref DMADRV_DeInit() @n
1669 ///    These functions initialize or deinitialize the DMADRV driver. Typically,
1670 ///    DMADRV_Init() is called once in the startup code.
1671 ///
1672 ///   @ref DMADRV_AllocateChannel(), @ref DMADRV_FreeChannel() @n
1673 ///    DMA channel reserve and release functions. It is recommended that
1674 ///    application code check that DMADRV_AllocateChannel()
1675 ///    returns ECODE_EMDRV_DMADRV_OK before starting a DMA
1676 ///    transfer.
1677 ///
1678 ///   @ref DMADRV_MemoryPeripheral() @n
1679 ///    Start a DMA transfer from memory to a peripheral.
1680 ///
1681 ///   @ref DMADRV_PeripheralMemory() @n
1682 ///    Start a DMA transfer from a peripheral to memory.
1683 ///
1684 ///   @ref DMADRV_MemoryPeripheralPingPong() @n
1685 ///    Start a DMA ping-pong transfer from memory to a peripheral.
1686 ///
1687 ///   @ref DMADRV_PeripheralMemoryPingPong() @n
1688 ///    Start a DMA ping-pong transfer from a peripheral to memory.
1689 ///
1690 ///   @ref DMADRV_LdmaStartTransfer() @n
1691 ///    Start a DMA transfer on an LDMA controller.
1692 ///
1693 ///   @ref DMADRV_PauseTransfer() @n
1694 ///    Pause an ongoing DMA transfer.
1695 ///
1696 ///   @ref DMADRV_ResumeTransfer() @n
1697 ///    Resume paused DMA transfer.
1698 ///
1699 ///   @ref DMADRV_StopTransfer() @n
1700 ///    Stop an ongoing DMA transfer.
1701 ///
1702 ///   @ref DMADRV_TransferActive() @n
1703 ///    Check if a transfer is ongoing.
1704 ///
1705 ///   @ref DMADRV_TransferCompletePending() @n
1706 ///    Check if a transfer completion is pending.
1707 ///
1708 ///   @ref DMADRV_TransferDone() @n
1709 ///    Check if a transfer has completed.
1710 ///
1711 ///   @ref DMADRV_TransferRemainingCount() @n
1712 ///    Get number of items remaining in a transfer.
1713 ///
1714 ///   @n @section dmadrv_example Example
1715 ///   Transfer a text string to USART1.
1716 ///   @code{.c}
1717 /// #include "dmadrv.h"
1718 ///
1719 ///   char str[] = "Hello DMA !";
1720 ///   unsigned int channel;
1721 ///
1722 ///   int main( void )
1723 ///   {
1724 ///   // Initialize DMA.
1725 ///   DMADRV_Init();
1726 ///
1727 ///   // Request a DMA channel.
1728 ///   DMADRV_AllocateChannel( &channel, NULL );
1729 ///
1730 ///   // Start the DMA transfer.
1731 ///   DMADRV_MemoryPeripheral( channel,
1732 ///                           dmadrvPeripheralSignal_USART1_TXBL,
1733 ///                           (void*)&(USART1->TXDATA),
1734 ///                           str,
1735 ///                           true,
1736 ///                           sizeof( str ),
1737 ///                           dmadrvDataSize1,
1738 ///                           NULL,
1739 ///                           NULL );
1740 ///
1741 ///   return 0;
1742 ///   }
1743 ///   @endcode
1744 ///
1745 /// @} end group dmadrv ********************************************************
1746