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