/**
 *******************************************************************************
 * @file  hc32_ll_dma.c
 * @brief This file provides firmware functions to manage the Direct Memory
 *        Access (DMA).
 @verbatim
   Change Logs:
   Date             Author          Notes
   2022-03-31       CDT             First version
   2022-06-30       CDT             Modify DMA_StructInit() default value
   2022-10-31       CDT             Modify DMA config API
   2023-01-15       CDT             Modify API DMA_DeInit and add LLP address assert
   2023-06-30       CDT             Modify typo
                                    Modify blocksize assert, 1024U is valid
                                    Add API DMA_SetDataWidth()
                                    Optimize set blocksize & repeat count process
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2022-2023, Xiaohua Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by XHSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "hc32_ll_dma.h"
#include "hc32_ll_utility.h"

/**
 * @addtogroup LL_Driver
 * @{
 */

/**
 * @defgroup LL_DMA DMA
 * @brief Direct Memory Access Driver Library
 * @{
 */

#if (LL_DMA_ENABLE == DDL_ON)

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/**
 * @defgroup DMA_Local_Macros DMA Local Macros
 * @{
 */
#define DMA_CH_REG(reg_base, ch)        (*(__IO uint32_t *)((uint32_t)(&(reg_base)) + ((ch) * 0x40UL)))

#define DMA_CNT                         (10U)
#define DMA_IDLE                        (0U)
#define DMA_BUSY                        (1U)
#define DMATIMEOUT1                     (0x5000U)
#define DMATIMEOUT2                     (0x1000u)

/**
 * @defgroup DMA_Check_Parameters_Validity DMA Check Parameters Validity
 * @{
 */
/* Parameter valid check for DMA unit. */
#define IS_DMA_UNIT(x)                                                          \
(   ((x) == CM_DMA1)                        ||                                  \
    ((x) == CM_DMA2))

/* Parameter valid check for DMA channel. */
#define IS_DMA_CH(x)                    ((x) <= DMA_CH3)

/* Parameter valid check for DMA multiplex channel. */
#define IS_DMA_MX_CH(x)                                                         \
(   ((x) != 0x00UL)                         &&                                  \
    (((x) | DMA_MX_CH_ALL) == DMA_MX_CH_ALL))

/* Parameter valid check for DMA block size. */
#define IS_DMA_BLOCK_SIZE(x)            ((x) <= 1024U)

/* Parameter valid check for DMA non-sequence transfer count. */
#define IS_DMA_NON_SEQ_TRANS_CNT(x)     ((x) < 4096U)

/* Parameter valid check for DMA non-sequence offset. */
#define IS_DMA_NON_SEQ_OFFSET(x)        ((x) <= ((1UL << 20U) - 1UL))

/* Parameter valid check for DMA LLP function. */
#define IS_DMA_LLP_EN(x)                                                        \
(   ((x) == DMA_LLP_ENABLE)                 ||                                  \
    ((x) == DMA_LLP_DISABLE))

/* Parameter valid check for DMA linked-list-pointer mode. */
#define IS_DMA_LLP_MD(x)                                                        \
(   ((x) == DMA_LLP_RUN)                    ||                                  \
    ((x) == DMA_LLP_WAIT))

/* Parameter valid check for address alignment of DMA linked-list-pointer descriptor  */
#define IS_DMA_LLP_ADDR_ALIGN(x)        IS_ADDR_ALIGN_WORD(x)

/* Parameter valid check for DMA error flag. */
#define IS_DMA_ERR_FLAG(x)                                                      \
(   ((x)!= 0x00000000UL)                    &&                                  \
    (((x)| DMA_FLAG_ERR_MASK) == DMA_FLAG_ERR_MASK))

/* Parameter valid check for DMA transfer flag. */
#define IS_DMA_TRANS_FLAG(x)                                                    \
(   ((x)!= 0x00000000UL)                    &&                                  \
    (((x)| DMA_FLAG_TRANS_MASK) == DMA_FLAG_TRANS_MASK))

/* Parameter valid check for DMA error interrupt. */
#define IS_DMA_ERR_INT(x)                                                       \
(   ((x)!= 0x00000000UL)                    &&                                  \
    (((x)| DMA_INT_ERR_MASK) == DMA_INT_ERR_MASK))

/* Parameter valid check for DMA transfer interrupt. */
#define IS_DMA_TRANS_INT(x)                                                     \
(   ((x)!= 0x00000000UL)                    &&                                  \
    (((x)| DMA_INT_TRANS_MASK) == DMA_INT_TRANS_MASK))

/* Parameter valid check for DMA request status. */
#define IS_DMA_REQ_STAT(x)                                                      \
(   ((x) != 0x00000000UL)                   &&                                  \
    (((x) | DMA_STAT_REQ_MASK) == DMA_STAT_REQ_MASK))

/* Parameter valid check for DMA transfer status. */
#define IS_DMA_TRANS_STAT(x)                                                    \
(   ((x) != 0x00000000UL)                   &&                                  \
    (((x) | DMA_STAT_TRANS_MASK) == DMA_STAT_TRANS_MASK))

/* Parameter valid check for DMA transfer data width. */
#define IS_DMA_DATA_WIDTH(x)                                                    \
(   ((x) == DMA_DATAWIDTH_8BIT)             ||                                  \
    ((x) == DMA_DATAWIDTH_16BIT)            ||                                  \
    ((x) == DMA_DATAWIDTH_32BIT))

/* Parameter valid check for DMA source address mode. */
#define IS_DMA_SADDR_MD(x)                                                      \
(   ((x) == DMA_SRC_ADDR_FIX)               ||                                  \
    ((x) == DMA_SRC_ADDR_INC)               ||                                  \
    ((x) == DMA_SRC_ADDR_DEC))

/* Parameter valid check for DMA destination address mode. */
#define IS_DMA_DADDR_MD(x)                                                      \
(   ((x) == DMA_DEST_ADDR_FIX)              ||                                  \
    ((x) == DMA_DEST_ADDR_INC)              ||                                  \
    ((x) == DMA_DEST_ADDR_DEC))

/* Parameter valid check for DMA repeat mode. */
#define IS_DMA_RPT_MD(x)                                                        \
(   ((x) == DMA_RPT_NONE)                   ||                                  \
    ((x) == DMA_RPT_SRC)                    ||                                  \
    ((x) == DMA_RPT_DEST)                   ||                                  \
    ((x) == DMA_RPT_BOTH))

/* Parameter valid check for DMA non_sequence mode. */
#define IS_DMA_NON_SEQ_MD(x)                                                    \
(   ((x) == DMA_NON_SEQ_NONE)               ||                                  \
    ((x) == DMA_NON_SEQ_SRC)                ||                                  \
    ((x) == DMA_NON_SEQ_DEST)               ||                                  \
    ((x) == DMA_NON_SEQ_BOTH))

/* Parameter valid check for DMA global interrupt function. */
#define IS_DMA_INT_FUNC(x)                                                      \
(   ((x) == DMA_INT_ENABLE)                 ||                                  \
    ((x) == DMA_INT_DISABLE))

/* Parameter valid check for DMA reconfig count mode. */
#define IS_DMA_RC_CNT_MD(x)                                                     \
(   ((x) == DMA_RC_CNT_KEEP)                ||                                  \
    ((x) == DMA_RC_CNT_SRC)                 ||                                  \
    ((x) == DMA_RC_CNT_DEST))

/* Parameter valid check for DMA reconfig destination address mode. */
#define IS_DMA_RC_DA_MD(x)                                                      \
(   ((x) == DMA_RC_DEST_ADDR_KEEP)          ||                                  \
    ((x) == DMA_RC_DEST_ADDR_NS)            ||                                  \
    ((x) == DMA_RC_DEST_ADDR_RPT))

/* Parameter valid check for DMA reconfig source address mode. */
#define IS_DMA_RC_SA_MD(x)                                                      \
(   ((x) == DMA_RC_SRC_ADDR_KEEP)           ||                                  \
    ((x) == DMA_RC_SRC_ADDR_NS)             ||                                  \
    ((x) == DMA_RC_SRC_ADDR_RPT))

/**
 * @}
 */

/**
 * @}
 */

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup DMA_Global_Functions DMA Global Functions
 * @{
 */

/**
 * @brief  DMA global function config.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] enNewState An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void DMA_Cmd(CM_DMA_TypeDef *DMAx, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    /* Global setting, ENABLE or DISABLE DMA */
    WRITE_REG32(DMAx->EN, enNewState);
}

/**
 * @brief  DMA error IRQ function config.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u32ErrInt DMA error IRQ flag.  @ref DMA_Int_Request_Err_Sel, @ref DMA_Int_Trans_Err_Sel
 * @param  [in] enNewState An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void DMA_ErrIntCmd(CM_DMA_TypeDef *DMAx, uint32_t u32ErrInt, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_ERR_INT(u32ErrInt));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    if (DISABLE == enNewState) {
        SET_REG32_BIT(DMAx->INTMASK0, u32ErrInt);
    } else {
        CLR_REG32_BIT(DMAx->INTMASK0, u32ErrInt);
    }
}

/**
 * @brief  Get DMA error flag.
 * @param  [in] DMAx        DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u32Flag     DMA error IRQ flag.  @ref DMA_Flag_Trans_Err_Sel, @ref DMA_Flag_Request_Err_Sel
 * @retval An @ref en_flag_status_t enumeration type value.
 * @note   Include transfer error flag & request error flag
 */
en_flag_status_t DMA_GetErrStatus(const CM_DMA_TypeDef *DMAx, uint32_t u32Flag)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_ERR_FLAG(u32Flag));

    return (0U != READ_REG32_BIT(DMAx->INTSTAT0, u32Flag) ? SET : RESET);
}

/**
 * @brief  Clear DMA error flag.
 * @param  [in] DMAx        DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u32Flag     DMA error IRQ flag. @ref DMA_Flag_Trans_Err_Sel, @ref DMA_Flag_Request_Err_Sel
 * @retval None
 * @note   Include transfer error flag & request error flag
 */
void DMA_ClearErrStatus(CM_DMA_TypeDef *DMAx, uint32_t u32Flag)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_ERR_FLAG(u32Flag));

    SET_REG32_BIT(DMAx->INTCLR0, u32Flag);
}

/**
 * @brief  DMA transfer IRQ function config.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u32TransCompleteInt DMA transfer complete IRQ flag. @ref DMA_Int_Btc_Sel, @ref DMA_Int_Tc_Sel
 * @param  [in] enNewState An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void DMA_TransCompleteIntCmd(CM_DMA_TypeDef *DMAx, uint32_t u32TransCompleteInt, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_TRANS_INT(u32TransCompleteInt));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    if (DISABLE == enNewState) {
        SET_REG32_BIT(DMAx->INTMASK1, u32TransCompleteInt);
    } else {
        CLR_REG32_BIT(DMAx->INTMASK1, u32TransCompleteInt);
    }
}

/**
 * @brief  Get DMA transfer flag.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u32Flag DMA transfer IRQ flag. @ref DMA_Flag_Btc_Sel, @ref DMA_Flag_Tc_Sel
 * @retval An @ref en_flag_status_t enumeration type value.
 * @note   Include transfer complete flag & block transfer complete flag
 */
en_flag_status_t DMA_GetTransCompleteStatus(const CM_DMA_TypeDef *DMAx, uint32_t u32Flag)
{
    DDL_ASSERT(IS_DMA_TRANS_FLAG(u32Flag));
    return ((0U != READ_REG32_BIT(DMAx->INTSTAT1, u32Flag)) ? SET : RESET);
}

/**
 * @brief  Clear DMA transfer flag.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u32Flag DMA transfer complete flag.  @ref DMA_Flag_Btc_Sel, @ref DMA_Flag_Tc_Sel
 * @retval None
 * @note   Include transfer complete flag & block transfer complete flag
 */
void DMA_ClearTransCompleteStatus(CM_DMA_TypeDef *DMAx, uint32_t u32Flag)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_TRANS_FLAG(u32Flag));

    SET_REG32_BIT(DMAx->INTCLR1, u32Flag);
}

/**
 * @brief  DMA channel function config.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] enNewState An @ref en_functional_state_t enumeration value.
 * @retval int32_t
 */
int32_t DMA_ChCmd(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, en_functional_state_t enNewState)
{
    static __IO uint8_t u8DmaChEnState = DMA_IDLE;

    uint16_t u16Timeout = 0U;
    uint32_t u32Temp;
    uint32_t u32Count;
    uint32_t u32MonCount;

    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    if (DMA_IDLE == u8DmaChEnState) {
        u8DmaChEnState = DMA_BUSY;

        /* Read back channel enable register except current channel */
        u32Temp = (DMAx->CHEN & (~(1UL << u8Ch)));
        if (0UL != u32Temp) {
            if (((DMAx->CHEN & DMA_CHEN_CHEN_0) == DMA_CHEN_CHEN_0) && (u8Ch != DMA_CH0)) {
                u32Count = (DMAx->DTCTL0 & DMA_DTCTL_CNT) >> DMA_DTCTL_CNT_POS;
                u32MonCount = (DMAx->MONDTCTL0 & DMA_MONDTCTL_CNT) >> DMA_MONDTCTL_CNT_POS;
                if (u32MonCount > DMA_CNT) {
                    /* not wait. */
                } else if (u32MonCount < u32Count) {
                    while (0UL != (DMAx->CHEN & DMA_CHEN_CHEN_0)) {
                        u16Timeout++;
                        if (u16Timeout > DMATIMEOUT1) {
                            u8DmaChEnState =  DMA_IDLE;
                            return LL_ERR_TIMEOUT;
                        }
                    }
                } else {
                    /* else */
                }
            }
            if (((DMAx->CHEN & DMA_CHEN_CHEN_1) == DMA_CHEN_CHEN_1) && (u8Ch != DMA_CH1)) {
                u32Count = (DMAx->DTCTL1 & DMA_DTCTL_CNT) >> DMA_DTCTL_CNT_POS;
                u32MonCount = (DMAx->MONDTCTL1 & DMA_MONDTCTL_CNT) >> DMA_MONDTCTL_CNT_POS;
                if (u32MonCount > DMA_CNT) {
                    /* not wait. */
                } else if (u32MonCount < u32Count) {
                    u16Timeout = 0U;
                    while (0UL != (DMAx->CHEN & DMA_CHEN_CHEN_1)) {
                        u16Timeout++;
                        if (u16Timeout > DMATIMEOUT1) {
                            u8DmaChEnState =  DMA_IDLE;
                            return LL_ERR_TIMEOUT;
                        }
                    }
                } else {
                    /* else */
                }
            }
            if (((DMAx->CHEN & DMA_CHEN_CHEN_2) == DMA_CHEN_CHEN_2) && (u8Ch != DMA_CH2)) {
                u16Timeout = 0U;
                u32Count = (DMAx->DTCTL2 & DMA_DTCTL_CNT) >> DMA_DTCTL_CNT_POS;
                u32MonCount = (DMAx->MONDTCTL2 & DMA_MONDTCTL_CNT) >> DMA_MONDTCTL_CNT_POS;
                if (u32MonCount > DMA_CNT) {
                    /* not wait. */
                } else if (u32MonCount < u32Count) {
                    while (0UL != (DMAx->CHEN & DMA_CHEN_CHEN_2)) {
                        u16Timeout++;
                        if (u16Timeout > DMATIMEOUT1) {
                            u8DmaChEnState =  DMA_IDLE;
                            return LL_ERR_TIMEOUT;
                        }
                    }
                } else {
                    /* else */
                }
            }
            if (((DMAx->CHEN & DMA_CHEN_CHEN_3) == DMA_CHEN_CHEN_3) && (u8Ch != DMA_CH3)) {
                u16Timeout = 0U;
                u32Count = (DMAx->DTCTL3 & DMA_DTCTL_CNT) >> DMA_DTCTL_CNT_POS;
                u32MonCount = (DMAx->MONDTCTL3 & DMA_MONDTCTL_CNT) >> DMA_MONDTCTL_CNT_POS;
                if (u32MonCount > DMA_CNT) {
                    /* not wait. */
                } else if (u32MonCount < u32Count) {
                    while (0UL != (DMAx->CHEN & DMA_CHEN_CHEN_3)) {
                        u16Timeout++;
                        if (u16Timeout > DMATIMEOUT1) {
                            u8DmaChEnState =  DMA_IDLE;
                            return LL_ERR_TIMEOUT;
                        }
                    }
                } else {
                    /* else */
                }
            }
        }

        if (ENABLE == enNewState) {
            DMAx->CHEN |= (1UL << u8Ch) & DMA_CHEN_CHEN;
        } else {
            DMAx->CHEN &= (~(1UL << u8Ch)) & DMA_CHEN_CHEN;
        }

        u8DmaChEnState =  DMA_IDLE;
        return LL_OK;
    }

    return LL_ERR;
}

/**
 * @brief  Get DMA transfer status.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u32Status DMA transfer status. @ref DMA_Trans_Status_Sel
 * @retval An @ref en_flag_status_t enumeration type value.
 */
en_flag_status_t DMA_GetTransStatus(const CM_DMA_TypeDef *DMAx, uint32_t u32Status)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_TRANS_STAT(u32Status));

    return ((0U != READ_REG32_BIT(DMAx->CHSTAT, u32Status)) ? SET : RESET);
}

/**
 * @brief  Get DMA request status.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u32Status DMA request status. @ref DMA_Req_Status_Sel
 * @retval An @ref en_flag_status_t enumeration type value.
 */
en_flag_status_t DMA_GetRequestStatus(const CM_DMA_TypeDef *DMAx, uint32_t u32Status)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_REQ_STAT(u32Status));

    return ((0U != READ_REG32_BIT(DMAx->REQSTAT, u32Status)) ? SET : RESET);
}

/**
 * @brief  Config DMA source address.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u32Addr DMA source address.
 * @retval int32_t
 */
int32_t DMA_SetSrcAddr(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint32_t u32Addr)
{
    uint16_t u16Timeout = 0U;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    WRITE_REG32(DMA_CH_REG(DMAx->SAR0, u8Ch), u32Addr);

    /* Ensure the address has been written */
    while (u32Addr != READ_REG32(DMA_CH_REG(DMAx->MONSAR0, u8Ch))) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            WRITE_REG32(DMA_CH_REG(DMAx->SAR0, u8Ch), u32Addr);
        }
    }
    return LL_OK;
}

/**
 * @brief  Config DMA destination address.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u32Addr DMA destination address.
 * @retval int32_t
 */
int32_t DMA_SetDestAddr(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint32_t u32Addr)
{
    uint16_t u16Timeout = 0U;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    WRITE_REG32(DMA_CH_REG(DMAx->DAR0, u8Ch), u32Addr);

    /* Ensure the address has been written */
    while (u32Addr != READ_REG32(DMA_CH_REG(DMAx->MONDAR0, u8Ch))) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            WRITE_REG32(DMA_CH_REG(DMAx->DAR0, u8Ch), u32Addr);
        }
    }
    return LL_OK;
}

/**
 * @brief  Config DMA transfer count.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u16Count DMA transfer count (0: infinite, 1 ~ 65535).
 * @retval int32_t
 */
int32_t DMA_SetTransCount(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint16_t u16Count)
{
    uint16_t u16Timeout = 0U;
    __IO uint32_t *MONDTCTLx;
    __IO uint32_t *DTCTLx;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    DTCTLx = &DMA_CH_REG(DMAx->DTCTL0, u8Ch);
    MODIFY_REG32(*DTCTLx, DMA_DTCTL_CNT, ((uint32_t)(u16Count) << DMA_DTCTL_CNT_POS));
    MONDTCTLx = &DMA_CH_REG(DMAx->MONDTCTL0, u8Ch);
    /* Ensure the transfer count has been written */
    while (u16Count != (READ_REG32_BIT(*MONDTCTLx, DMA_MONDTCTL_CNT) >> DMA_MONDTCTL_CNT_POS)) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            MODIFY_REG32(*DTCTLx, DMA_DTCTL_CNT, ((uint32_t)(u16Count) << DMA_DTCTL_CNT_POS));
        }
    }
    return LL_OK;
}

/**
 * @brief  Config DMA block size per transfer.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u16Size DMA block size (range: 0~1024, 0 is for 1024).
 * @retval int32_t
 */
int32_t DMA_SetBlockSize(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint16_t u16Size)
{
    uint16_t u16Timeout = 0U;
    __IO uint32_t *MONDTCTLx;
    __IO uint32_t *DTCTLx;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_BLOCK_SIZE(u16Size));

    DTCTLx = &DMA_CH_REG(DMAx->DTCTL0, u8Ch);
    MODIFY_REG32(*DTCTLx, DMA_DTCTL_BLKSIZE, u16Size);

    MONDTCTLx = &DMA_CH_REG(DMAx->MONDTCTL0, u8Ch);
    /* Ensure the block size has been written */
    while (u16Size != READ_REG32_BIT(*MONDTCTLx, DMA_MONDTCTL_BLKSIZE)) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            MODIFY_REG32(*DTCTLx, DMA_DTCTL_BLKSIZE, u16Size);
        }
    }

    return LL_OK;
}

/**
 * @brief  Config DMA data width per transfer.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u32DataWidth DMA data width. @ref DMA_DataWidth_Sel
 * @retval int32_t
 */
int32_t DMA_SetDataWidth(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint32_t u32DataWidth)
{
    __IO uint32_t *CHxCTL0;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_DATA_WIDTH(u32DataWidth));

    CHxCTL0 = &DMA_CH_REG(DMAx->CHCTL0, u8Ch);
    MODIFY_REG32(*CHxCTL0, DMA_CHCTL_HSIZE, u32DataWidth);

    return LL_OK;
}

/**
 * @brief  Config DMA source repeat size.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u16Size DMA source repeat size (0, 1024: 1024, 1 ~ 1023).
 * @retval int32_t
 */
int32_t DMA_SetSrcRepeatSize(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint16_t u16Size)
{
    uint16_t u16Timeout = 0U;
    __IO uint32_t *MONRPTx;
    __IO uint32_t *RPTx;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_BLOCK_SIZE(u16Size));

    RPTx = &DMA_CH_REG(DMAx->RPT0, u8Ch);
    MODIFY_REG32(*RPTx, DMA_RPT_SRPT, ((uint32_t)(u16Size) << DMA_RPT_SRPT_POS));

    MONRPTx = &DMA_CH_REG(DMAx->MONRPT0, u8Ch);
    /* Ensure the repeat size has been written */
    while (u16Size != (READ_REG32_BIT(*MONRPTx, DMA_MONRPT_SRPT) >> DMA_MONRPT_SRPT_POS)) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            MODIFY_REG32(*RPTx, DMA_RPT_SRPT, ((uint32_t)(u16Size) << DMA_RPT_SRPT_POS));
        }
    }

    return LL_OK;
}

/**
 * @brief  Config DMA destination repeat size.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u16Size DMA destination repeat size (0, 1024: 1024, 1 ~ 1023).
 * @retval int32_t
 */
int32_t DMA_SetDestRepeatSize(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint16_t u16Size)
{
    uint16_t u16Timeout = 0U;
    __IO uint32_t *MONRPTx;
    __IO uint32_t *RPTx;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_BLOCK_SIZE(u16Size));

    RPTx = &DMA_CH_REG(DMAx->RPT0, u8Ch);
    MODIFY_REG32(*RPTx, DMA_RPT_DRPT, ((uint32_t)(u16Size) << DMA_RPT_DRPT_POS));

    MONRPTx = &DMA_CH_REG(DMAx->MONRPT0, u8Ch);
    /* Ensure the repeat size has been written */
    while (u16Size != (READ_REG32_BIT(*MONRPTx, DMA_MONRPT_DRPT) >> DMA_MONRPT_DRPT_POS)) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            MODIFY_REG32(*RPTx, DMA_RPT_DRPT, ((uint32_t)(u16Size) << DMA_RPT_DRPT_POS));
        }
    }

    return LL_OK;
}

/**
 * @brief  Config DMA source transfer count under non-sequence mode.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u32Count DMA source transfer count (0, 4096: 4096, 1 ~ 4095).
 * @retval int32_t
 */
int32_t DMA_SetNonSeqSrcCount(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint32_t u32Count)
{
    uint16_t u16Timeout = 0U;
    __IO uint32_t *MONSNSEQCTLx;
    __IO uint32_t *SNSEQCTLx;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_NON_SEQ_TRANS_CNT(u32Count));

    SNSEQCTLx = &DMA_CH_REG(DMAx->SNSEQCTL0, u8Ch);
    MODIFY_REG32(*SNSEQCTLx, DMA_SNSEQCTL_SNSCNT, (u32Count << DMA_SNSEQCTL_SNSCNT_POS));

    MONSNSEQCTLx = &DMA_CH_REG(DMAx->MONSNSEQCTL0, u8Ch);
    /* Ensure the count has been written */
    while (u32Count != (READ_REG32_BIT(*MONSNSEQCTLx, DMA_MONSNSEQCTL_SNSCNT) >> DMA_MONSNSEQCTL_SNSCNT_POS)) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            MODIFY_REG32(*SNSEQCTLx, DMA_SNSEQCTL_SNSCNT, (u32Count << DMA_SNSEQCTL_SNSCNT_POS));
        }
    }

    return LL_OK;
}

/**
 * @brief  Config DMA destination transfer count under non-sequence mode.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u32Count DMA destination transfer count (0, 4096: 4096, 1 ~ 4095).
 * @retval int32_t
 */
int32_t DMA_SetNonSeqDestCount(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint32_t u32Count)
{
    uint16_t u16Timeout = 0U;
    __IO uint32_t *MONDNSEQCTLx;
    __IO uint32_t *DNSEQCTLx;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_NON_SEQ_TRANS_CNT(u32Count));

    DNSEQCTLx = &DMA_CH_REG(DMAx->DNSEQCTL0, u8Ch);
    MODIFY_REG32(*DNSEQCTLx, DMA_DNSEQCTL_DNSCNT, (u32Count << DMA_DNSEQCTL_DNSCNT_POS));

    MONDNSEQCTLx = &DMA_CH_REG(DMAx->MONDNSEQCTL0, u8Ch);
    /* Ensure the count has been written */
    while (u32Count != (READ_REG32_BIT(*MONDNSEQCTLx, DMA_MONDNSEQCTL_DNSCNT) >> DMA_MONDNSEQCTL_DNSCNT_POS)) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            MODIFY_REG32(*DNSEQCTLx, DMA_DNSEQCTL_DNSCNT, (u32Count << DMA_DNSEQCTL_DNSCNT_POS));
        }
    }

    return LL_OK;
}

/**
 * @brief  Config DMA source offset number under non-sequence mode.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u32Offset DMA source offset (0 ~ 2^20 - 1).
 * @retval int32_t
 */
int32_t DMA_SetNonSeqSrcOffset(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint32_t u32Offset)
{
    uint16_t u16Timeout = 0U;
    __IO uint32_t *MONSNSEQCTLx;
    __IO uint32_t *SNSEQCTLx;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_NON_SEQ_OFFSET(u32Offset));

    SNSEQCTLx = &DMA_CH_REG(DMAx->SNSEQCTL0, u8Ch);
    MODIFY_REG32(*SNSEQCTLx, DMA_SNSEQCTL_SOFFSET, u32Offset);

    MONSNSEQCTLx = &DMA_CH_REG(DMAx->MONSNSEQCTL0, u8Ch);
    /* Ensure the offset has been written */
    while (u32Offset != READ_REG32_BIT(*MONSNSEQCTLx, DMA_MONSNSEQCTL_SOFFSET)) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            MODIFY_REG32(*SNSEQCTLx, DMA_SNSEQCTL_SOFFSET, u32Offset);
        }
    }

    return LL_OK;
}

/**
 * @brief  Config DMA destination offset number under non-sequence mode.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u32Offset DMA destination offset (0 ~ 2^20 - 1).
 * @retval int32_t
 */
int32_t DMA_SetNonSeqDestOffset(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint32_t u32Offset)
{
    uint16_t u16Timeout = 0U;
    __IO uint32_t *MONDNSEQCTLx;
    __IO uint32_t *DNSEQCTLx;
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_NON_SEQ_OFFSET(u32Offset));

    DNSEQCTLx = &DMA_CH_REG(DMAx->DNSEQCTL0, u8Ch);
    MODIFY_REG32(*DNSEQCTLx, DMA_DNSEQCTL_DOFFSET, u32Offset);

    MONDNSEQCTLx = &DMA_CH_REG(DMAx->MONDNSEQCTL0, u8Ch);
    /* Ensure the offset has been written */
    while (u32Offset != READ_REG32_BIT(*MONDNSEQCTLx, DMA_MONDNSEQCTL_DOFFSET)) {
        u16Timeout++;
        if (u16Timeout > DMATIMEOUT2) {
            return LL_ERR_TIMEOUT;
        } else {
            MODIFY_REG32(*DNSEQCTLx, DMA_DNSEQCTL_DOFFSET, u32Offset);
        }
    }

    return LL_OK;
}

/**
 * @brief  De-Initialize DMA channel function.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
  * @retval None
 */
void DMA_DeInit(CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    /* Disable */
    CLR_REG32_BIT(DMAx->CHEN, DMA_CHEN_CHEN_0 << u8Ch);

    /* Set default value. */
    WRITE_REG32(DMA_CH_REG(DMAx->SAR0, u8Ch), 0UL);
    WRITE_REG32(DMA_CH_REG(DMAx->DAR0, u8Ch), 0UL);
    CLR_REG32_BIT(DMAx->INTMASK0, (DMA_INTMASK0_MSKTRNERR_0 | DMA_INTMASK0_MSKREQERR_0) << u8Ch);
    CLR_REG32_BIT(DMAx->INTMASK1, (DMA_INTMASK1_MSKTC_0 | DMA_INTMASK1_MSKBTC_0) << u8Ch);
    SET_REG32_BIT(DMAx->INTCLR0, (DMA_INTCLR0_CLRTRNERR_0 | DMA_INTCLR0_CLRREQERR_0) << u8Ch);
    SET_REG32_BIT(DMAx->INTCLR1, (DMA_INTCLR1_CLRTC_0 | DMA_INTCLR1_CLRBTC_0) << u8Ch);

    WRITE_REG32(DMA_CH_REG(DMAx->DTCTL0, u8Ch), 1UL);
    WRITE_REG32(DMA_CH_REG(DMAx->CHCTL0, u8Ch), 0x00001000UL);
    WRITE_REG32(DMA_CH_REG(DMAx->RPT0, u8Ch), 0UL);
    WRITE_REG32(DMA_CH_REG(DMAx->SNSEQCTL0, u8Ch), 0UL);
    WRITE_REG32(DMA_CH_REG(DMAx->DNSEQCTL0, u8Ch), 0UL);
    WRITE_REG32(DMA_CH_REG(DMAx->LLP0, u8Ch), 0UL);

}

/**
 * @brief  Initialize DMA config structure. Fill each pstcDmaInit with default value
 * @param  [in] pstcDmaInit Pointer to a stc_dma_init_t structure that
 *                            contains configuration information.
 * @retval int32_t:
 *         - LL_OK: DMA structure initialize successful
 *         - LL_ERR_INVD_PARAM: NULL pointer
 */
int32_t DMA_StructInit(stc_dma_init_t *pstcDmaInit)
{
    int32_t i32Ret = LL_OK;

    if (NULL == pstcDmaInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        pstcDmaInit->u32IntEn       = DMA_INT_DISABLE;
        pstcDmaInit->u32SrcAddr     = 0x00UL;
        pstcDmaInit->u32DestAddr    = 0x00UL;
        pstcDmaInit->u32DataWidth   = DMA_DATAWIDTH_8BIT;
        pstcDmaInit->u32BlockSize   = 0x01UL;
        pstcDmaInit->u32TransCount  = 0x00UL;
        pstcDmaInit->u32SrcAddrInc  = DMA_SRC_ADDR_FIX;
        pstcDmaInit->u32DestAddrInc = DMA_DEST_ADDR_FIX;
    }
    return i32Ret;
}

/**
 * @brief  DMA basic function initialize.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] pstcDmaInit DMA config structure.
 *   @arg  u32IntEn         DMA interrupt ENABLE or DISABLE.
 *   @arg  u32SrcAddr       DMA source address.
 *   @arg  u32DestAddr      DMA destination address.
 *   @arg  u32DataWidth     DMA data width.
 *   @arg  u32BlockSize     DMA block size.
 *   @arg  u32TransCount    DMA transfer count.
 *   @arg  u32SrcAddrInc    DMA source address direction.
 *   @arg  u32DestAddrInc   DMA destination address direction.
 * @retval int32_t:
 *          - LL_OK: DMA basic function initialize successful
 *          - LL_ERR_INVD_PARAM: NULL pointer
 */
int32_t DMA_Init(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, const stc_dma_init_t *pstcDmaInit)
{
    int32_t i32Ret = LL_OK;
    __IO uint32_t *CHCTLx;

    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    if (NULL == pstcDmaInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        DDL_ASSERT(IS_DMA_DATA_WIDTH(pstcDmaInit->u32DataWidth));
        DDL_ASSERT(IS_DMA_SADDR_MD(pstcDmaInit->u32SrcAddrInc));
        DDL_ASSERT(IS_DMA_DADDR_MD(pstcDmaInit->u32DestAddrInc));
        DDL_ASSERT(IS_DMA_BLOCK_SIZE(pstcDmaInit->u32BlockSize));
        DDL_ASSERT(IS_DMA_INT_FUNC(pstcDmaInit->u32IntEn));

        WRITE_REG32(DMA_CH_REG(DMAx->SAR0, u8Ch), pstcDmaInit->u32SrcAddr);
        WRITE_REG32(DMA_CH_REG(DMAx->DAR0, u8Ch), pstcDmaInit->u32DestAddr);

        WRITE_REG32(DMA_CH_REG(DMAx->DTCTL0, u8Ch), ((pstcDmaInit->u32BlockSize & DMA_DTCTL_BLKSIZE) | \
                                                     (pstcDmaInit->u32TransCount << DMA_DTCTL_CNT_POS)));

        CHCTLx = &DMA_CH_REG(DMAx->CHCTL0, u8Ch);
        MODIFY_REG32(*CHCTLx, (DMA_CHCTL_SINC | DMA_CHCTL_DINC | DMA_CHCTL_HSIZE | DMA_CHCTL_IE),       \
                     (pstcDmaInit->u32IntEn | pstcDmaInit->u32DataWidth | pstcDmaInit->u32SrcAddrInc | \
                      pstcDmaInit->u32DestAddrInc));

    }
    return i32Ret;
}

/**
 * @brief  Initialize DMA repeat mode config structure.
 *          Fill each pstcDmaInit with default value
 * @param  [in] pstcDmaRepeatInit Pointer to a stc_dma_repeat_init_t structure that
 *                            contains configuration information.
 * @retval int32_t:
 *          - LL_OK: DMA repeat mode config structure initialize successful
 *          - LL_ERR_INVD_PARAM: NULL pointer
 */
int32_t DMA_RepeatStructInit(stc_dma_repeat_init_t *pstcDmaRepeatInit)
{
    int32_t i32Ret = LL_OK;

    if (NULL == pstcDmaRepeatInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        pstcDmaRepeatInit->u32Mode       = DMA_RPT_NONE;
        pstcDmaRepeatInit->u32SrcCount  = 0x00UL;
        pstcDmaRepeatInit->u32DestCount = 0x00UL;
    }
    return i32Ret;
}

/**
 * @brief  DMA repeat mode initialize.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] pstcDmaRepeatInit DMA repeat mode config structure.
 * @note Call this function after DMA_Init();
 */
int32_t DMA_RepeatInit(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, const stc_dma_repeat_init_t *pstcDmaRepeatInit)
{
    int32_t i32Ret = LL_OK;
    __IO uint32_t *CHCTLx;

    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    if (NULL == pstcDmaRepeatInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        DDL_ASSERT(IS_DMA_RPT_MD(pstcDmaRepeatInit->u32Mode));
        DDL_ASSERT(IS_DMA_BLOCK_SIZE(pstcDmaRepeatInit->u32DestCount));
        DDL_ASSERT(IS_DMA_BLOCK_SIZE(pstcDmaRepeatInit->u32SrcCount));

        CHCTLx = &DMA_CH_REG(DMAx->CHCTL0, u8Ch);
        MODIFY_REG32(*CHCTLx, (DMA_CHCTL_SRPTEN | DMA_CHCTL_DRPTEN), pstcDmaRepeatInit->u32Mode);

        WRITE_REG32(DMA_CH_REG(DMAx->RPT0, u8Ch), \
                    ((pstcDmaRepeatInit->u32DestCount << DMA_RPT_DRPT_POS) | pstcDmaRepeatInit->u32SrcCount) & \
                    (DMA_RPT_DRPT | DMA_RPT_SRPT));

    }
    return i32Ret;
}

/**
 * @brief  Initialize DMA non-sequence mode config structure.
 *          Fill each pstcDmaInit with default value
 * @param  [in] pstcDmaNonSeqInit Pointer to a stc_dma_nonseq_init_t structure that
 *                            contains configuration information.
 * @retval int32_t:
 *          - LL_OK: DMA non-sequence mode structure initialize successful
 *          - LL_ERR_INVD_PARAM: NULL pointer
 */
int32_t DMA_NonSeqStructInit(stc_dma_nonseq_init_t *pstcDmaNonSeqInit)
{
    int32_t i32Ret = LL_OK;

    if (NULL == pstcDmaNonSeqInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        pstcDmaNonSeqInit->u32Mode       = DMA_NON_SEQ_NONE;
        pstcDmaNonSeqInit->u32SrcCount   = 0x00UL;
        pstcDmaNonSeqInit->u32SrcOffset  = 0x00UL;
        pstcDmaNonSeqInit->u32DestCount  = 0x00UL;
        pstcDmaNonSeqInit->u32DestOffset = 0x00UL;
    }
    return i32Ret;
}

/**
 * @brief  DMA non-sequence mode initialize.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] pstcDmaNonSeqInit DMA non-sequence mode config structure.
 * @retval int32_t:
 *          - LL_OK: DMA non-sequence function initialize successful
 *          - LL_ERR_INVD_PARAM: NULL pointer
 * @note Call this function after DMA_Init();
 */
int32_t DMA_NonSeqInit(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, const stc_dma_nonseq_init_t *pstcDmaNonSeqInit)
{
    int32_t i32Ret = LL_OK;
    __IO uint32_t *CHCTLx;

    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    if (NULL == pstcDmaNonSeqInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        DDL_ASSERT(IS_DMA_NON_SEQ_MD(pstcDmaNonSeqInit->u32Mode));

        DDL_ASSERT(IS_DMA_NON_SEQ_TRANS_CNT(pstcDmaNonSeqInit->u32SrcCount));
        DDL_ASSERT(IS_DMA_NON_SEQ_TRANS_CNT(pstcDmaNonSeqInit->u32DestCount));
        DDL_ASSERT(IS_DMA_NON_SEQ_OFFSET(pstcDmaNonSeqInit->u32SrcOffset));
        DDL_ASSERT(IS_DMA_NON_SEQ_OFFSET(pstcDmaNonSeqInit->u32DestOffset));

        CHCTLx = &DMA_CH_REG(DMAx->CHCTL0, u8Ch);
        MODIFY_REG32(*CHCTLx, (DMA_CHCTL_SNSEQEN | DMA_CHCTL_DNSEQEN), pstcDmaNonSeqInit->u32Mode);

        WRITE_REG32(DMA_CH_REG(DMAx->SNSEQCTL0, u8Ch), ((pstcDmaNonSeqInit->u32SrcCount << DMA_SNSEQCTL_SNSCNT_POS) | \
                                                        pstcDmaNonSeqInit->u32SrcOffset));
        WRITE_REG32(DMA_CH_REG(DMAx->DNSEQCTL0, u8Ch), ((pstcDmaNonSeqInit->u32DestCount << DMA_DNSEQCTL_DNSCNT_POS) | \
                                                        pstcDmaNonSeqInit->u32DestOffset));

    }
    return i32Ret;
}

/**
 * @brief  Initialize DMA Linked List Pointer (hereafter, LLP) mode config structure.
 *          Fill each pstcDmaInit with default value
 * @param  [in] pstcDmaLlpInit Pointer to a stc_dma_llp_init_t structure that
 *                            contains configuration information.
 * @retval int32_t:
 *          - LL_OK: DMA LLP mode config structure initialize successful
 *          - LL_ERR_INVD_PARAM: NULL pointer
 */
int32_t DMA_LlpStructInit(stc_dma_llp_init_t *pstcDmaLlpInit)
{
    int32_t i32Ret = LL_OK;

    if (NULL == pstcDmaLlpInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        pstcDmaLlpInit->u32State = DMA_LLP_DISABLE;
        pstcDmaLlpInit->u32Mode  = DMA_LLP_WAIT;
        pstcDmaLlpInit->u32Addr  = 0x00UL;
    }
    return i32Ret;
}

/**
 * @brief  DMA LLP mode initialize.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] pstcDmaLlpInit DMA LLP config structure.
 *   @arg  u32State      DMA LLP ENABLE or DISABLE.
 *   @arg  u32Mode       DMA LLP auto-run or wait request.
 *   @arg  u32Addr       DMA LLP next list pointer address.
 *   @arg  u32AddrSelect DMA LLP address mode.
 * @retval int32_t:
 *          - LL_OK: DMA LLP function initialize successful
 *          - LL_ERR_INVD_PARAM: NULL pointer
 * @note Call this function after DMA_Init();
 */
int32_t DMA_LlpInit(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, const stc_dma_llp_init_t *pstcDmaLlpInit)
{
    int32_t i32Ret = LL_OK;
    __IO uint32_t *CHCTLx;

    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    if (NULL == pstcDmaLlpInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        DDL_ASSERT(IS_DMA_LLP_EN(pstcDmaLlpInit->u32State));
        DDL_ASSERT(IS_DMA_LLP_MD(pstcDmaLlpInit->u32Mode));
        DDL_ASSERT(IS_DMA_LLP_ADDR_ALIGN(pstcDmaLlpInit->u32Addr));

        CHCTLx = &DMA_CH_REG(DMAx->CHCTL0, u8Ch);
        MODIFY_REG32(*CHCTLx, (DMA_CHCTL_LLPEN | DMA_CHCTL_LLPRUN), \
                     (pstcDmaLlpInit->u32State | pstcDmaLlpInit->u32Mode));

        WRITE_REG32(DMA_CH_REG(DMAx->LLP0, u8Ch), pstcDmaLlpInit->u32Addr & DMA_LLP_LLP);

    }

    return i32Ret;
}

/**
 * @brief  Config DMA LLP value.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] u32Addr Next link pointer address for DMA LLP mode.
 * @retval None
 */
void DMA_SetLlpAddr(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, uint32_t u32Addr)
{

    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_DMA_LLP_ADDR_ALIGN(u32Addr));

    WRITE_REG32(DMA_CH_REG(DMAx->LLP0, u8Ch), (u32Addr & DMA_LLP_LLP));
}

/**
 * @brief  DMA LLP ENABLE or DISABLE.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] enNewState An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void DMA_LlpCmd(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    if (ENABLE == enNewState) {
        SET_REG32_BIT(DMA_CH_REG(DMAx->CHCTL0, u8Ch), DMA_CHCTL_LLPEN);

    } else {
        CLR_REG32_BIT(DMA_CH_REG(DMAx->CHCTL0, u8Ch), DMA_CHCTL_LLPEN);

    }
}

/**
 * @brief  DMA reconfig function ENABLE or DISABLE.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] enNewState An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void DMA_ReconfigCmd(CM_DMA_TypeDef *DMAx, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    if (ENABLE == enNewState) {
        SET_REG32_BIT(DMAx->RCFGCTL, 1UL);
    } else {
        CLR_REG32_BIT(DMAx->RCFGCTL, 1UL);
    }
}

/**
 * @brief  DMA LLP ENABLE or DISABLE  for reconfig function.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] enNewState An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void DMA_ReconfigLlpCmd(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    MODIFY_REG32(DMAx->RCFGCTL, DMA_RCFGCTL_RCFGCHS | DMA_RCFGCTL_RCFGLLP, \
                 ((uint32_t)(u8Ch) << DMA_RCFGCTL_RCFGCHS_POS) | ((uint32_t)enNewState << DMA_RCFGCTL_RCFGLLP_POS));
}

/**
 * @brief  Initialize DMA re-config mode config structure.
 *          Fill each pstcDmaRCInit with default value
 * @param  [in] pstcDmaRCInit Pointer to a stc_dma_reconfig_init_t structure that
 *                            contains configuration information.
 * @retval int32_t:
 *          - LL_OK: DMA reconfig mode config structure initialize successful
 *          - LL_ERR_INVD_PARAM: NULL pointer
 */
int32_t DMA_ReconfigStructInit(stc_dma_reconfig_init_t *pstcDmaRCInit)
{
    int32_t i32Ret = LL_OK;

    if (NULL == pstcDmaRCInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        pstcDmaRCInit->u32CountMode         = DMA_RC_CNT_KEEP;
        pstcDmaRCInit->u32DestAddrMode      = DMA_RC_DEST_ADDR_KEEP;
        pstcDmaRCInit->u32SrcAddrMode       = DMA_RC_SRC_ADDR_KEEP;
    }
    return i32Ret;
}

/**
 * @brief  DMA reconfig mode initialize.
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @param  [in] pstcDmaRCInit DMA reconfig mode config structure
 *   @arg  u32CountMode         DMA reconfig count mode.
 *   @arg  u32DestAddrMode      DMA reconfig destination address mode.
 *   @arg  u32SrcAddrMode       DMA reconfig source address mode.
 * @retval int32_t:
 *          - LL_OK: DMA reconfig function initialize successful
 *          - LL_ERR_INVD_PARAM: NULL pointer
*/
int32_t DMA_ReconfigInit(CM_DMA_TypeDef *DMAx, uint8_t u8Ch, const stc_dma_reconfig_init_t *pstcDmaRCInit)
{
    int32_t i32Ret = LL_OK;

    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    if (NULL == pstcDmaRCInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        DDL_ASSERT(IS_DMA_RC_CNT_MD(pstcDmaRCInit->u32CountMode));
        DDL_ASSERT(IS_DMA_RC_DA_MD(pstcDmaRCInit->u32DestAddrMode));
        DDL_ASSERT(IS_DMA_RC_SA_MD(pstcDmaRCInit->u32SrcAddrMode));

        MODIFY_REG32(DMAx->RCFGCTL,                                                                     \
                     (DMA_RCFGCTL_RCFGCHS | DMA_RCFGCTL_SARMD | DMA_RCFGCTL_DARMD | DMA_RCFGCTL_CNTMD), \
                     (pstcDmaRCInit->u32CountMode | pstcDmaRCInit->u32SrcAddrMode |                     \
                      pstcDmaRCInit->u32DestAddrMode | ((uint32_t)(u8Ch) << DMA_RCFGCTL_RCFGCHS_POS)));
    }
    return i32Ret;
}

/**
 * @brief  DMA get current source address
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current source address.
 */
uint32_t DMA_GetSrcAddr(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return READ_REG32(DMA_CH_REG(DMAx->MONSAR0, u8Ch));
}

/**
 * @brief  DMA get current destination address
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current destination address.
 */
uint32_t DMA_GetDestAddr(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return READ_REG32(DMA_CH_REG(DMAx->MONDAR0, u8Ch));
}

/**
 * @brief  DMA get current transfer count
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current transfer count.
 */
uint32_t DMA_GetTransCount(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return ((READ_REG32(DMA_CH_REG(DMAx->MONDTCTL0, u8Ch)) >> DMA_DTCTL_CNT_POS) & 0xFFFFUL);
}

/**
 * @brief  DMA get current block size
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current block size.
 */
uint32_t DMA_GetBlockSize(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return (READ_REG32_BIT(DMA_CH_REG(DMAx->MONDTCTL0, u8Ch), DMA_DTCTL_BLKSIZE));
}

/**
 * @brief  DMA get current source repeat size
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current source repeat size.
 */
uint32_t DMA_GetSrcRepeatSize(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return (READ_REG32_BIT(DMA_CH_REG(DMAx->MONRPT0, u8Ch), DMA_RPT_SRPT));
}

/**
 * @brief  DMA get current destination repeat size
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current destination repeat size.
 */
uint32_t DMA_GetDestRepeatSize(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return ((READ_REG32(DMA_CH_REG(DMAx->MONRPT0, u8Ch)) >> DMA_RPT_DRPT_POS) & 0x3FFUL);
}

/**
 * @brief  DMA get current source count in non-sequence mode
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current source count in non-sequence mode.
 */
uint32_t DMA_GetNonSeqSrcCount(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return ((READ_REG32(DMA_CH_REG(DMAx->MONSNSEQCTL0, u8Ch)) >> DMA_SNSEQCTLB_SNSCNTB_POS) & 0xFFFUL);
}

/**
 * @brief  DMA get current destination count in non-sequence mode
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA,, x can be 0-1
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current destination count in non-sequence mode.
 */
uint32_t DMA_GetNonSeqDestCount(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return ((READ_REG32(DMA_CH_REG(DMAx->MONDNSEQCTL0, u8Ch)) >> DMA_DNSEQCTL_DNSCNT_POS) & 0xFFFUL);
}

/**
 * @brief  DMA get current source offset in non-sequence mode
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current source offset in non-sequence mode.
 */
uint32_t DMA_GetNonSeqSrcOffset(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return (READ_REG32_BIT(DMA_CH_REG(DMAx->MONSNSEQCTL0, u8Ch), DMA_SNSEQCTL_SOFFSET));
}

/**
 * @brief  DMA get current destination offset in non-sequence mode
 * @param  [in] DMAx DMA unit instance.
 *   @arg  CM_DMAx or CM_DMA
 * @param  [in] u8Ch DMA channel. @ref DMA_Channel_selection
 * @retval Current destination offset in non-sequence mode.
 */
uint32_t DMA_GetNonSeqDestOffset(const CM_DMA_TypeDef *DMAx, uint8_t u8Ch)
{
    DDL_ASSERT(IS_DMA_UNIT(DMAx));
    DDL_ASSERT(IS_DMA_CH(u8Ch));

    return (READ_REG32_BIT(DMA_CH_REG(DMAx->MONDNSEQCTL0, u8Ch), DMA_DNSEQCTL_DOFFSET));
}

/**
 * @}
 */

#endif  /* LL_DMA_ENABLE */

/**
 * @}
 */

/**
 * @}
 */

/******************************************************************************
 * EOF (not truncated)
 *****************************************************************************/
