#include "lks32mc09x_dma.h"
#include "lks32mc09x_sys.h"

/**
 * @brief 初始化DMA模块
 *
 * @param DMAx DMA寄存器基地址
 * @param DMAInitStruct 初始化结构体指针
 */

void DMA_Init(DMA_TypeDef *DMAx, DMA_InitTypeDef *DMAInitStruct)
{
    DMAx->CCR  = 0;
    DMAx->REN  = DMAInitStruct->REN;
    DMAx->SADR = DMAInitStruct->SADR;
    DMAx->DADR = DMAInitStruct->DADR;

    if (DMAInitStruct->TMS == 0)
    {
        DMAInitStruct->TMS = 1;
    }
    else
    {
        __NOP();
    }
    if ((DMAx == DMA1) || (DMAx == DMA2))
    {
        if (DMAInitStruct->TMS >= 0x100)
        {
            DMAInitStruct->TMS = 0xff;
        }
    }
    else if (DMAInitStruct->TMS >= 0x1000)
    {
        DMAInitStruct->TMS = 0xfff;
    }
    else
    {
        __NOP();
    }
    do
    {
        DMAx->CTMS = DMAInitStruct->TMS;
    } while (DMAx->CTMS != DMAInitStruct->TMS);

    DMAx->CCR = (DMAInitStruct->SBTW << 10) |
                (DMAInitStruct->DBTW << 8) |
                (DMAInitStruct->SINC << 6) |
                (DMAInitStruct->DINC << 4) |
                (DMAInitStruct->CIRC << 3) |
                (DMAInitStruct->RMODE << 1) |
                DMAInitStruct->EN;
}

/**
 * @brief 将DMA初始化结构体重置为默认值
 *
 * @param DMAInitStruct 初始化结构体指针
 */
void DMA_StructInit(DMA_InitTypeDef *DMAInitStruct)
{
    for (int i = 0; i < sizeof(DMA_InitTypeDef); i++)
    {
        ((uint8_t *)DMAInitStruct)[i] = 0;
    }
}

/**
 * @brief 获取DMA中断标志位状态
 *
 * @param DMA_IF_x 中断标志位MASK
 * @return 中断标志位状态
 */
uint32_t DMA_GetIRQFlag(uint32_t DMA_IF_x)
{
    return (DMA_TOP->IF & DMA_IF_x) ? 1 : 0;
}

/**
 * @brief 清除DMA中断标志位
 *
 * @param DMA_IF_x 中断标志位MASK
 */
void DMA_ClearIRQFlag(uint32_t DMA_IF_x)
{
    DMA_TOP->IF = DMA_IF_x;
}

/**
 * @brief 获取DMA标志位状态
 *
 * @param DMA_IF_x 标志位MASK
 * @return 标志位状态
 */
uint32_t DMA_GetFlag(uint32_t DMA_IF_x)
{
    // DMA标志位和中断标志位共用同一个寄存器
    return DMA_GetIRQFlag(DMA_IF_x);
}

/**
 * @brief 使能DMA模块
 */
void DMA_Enable(void)
{
    DMA_TOP->CTRL |= 0x01;
}

/**
 * @brief 禁用DMA模块
 */
void DMA_Disable(void)
{
    DMA_TOP->CTRL &= ~0x01;
}

/**
 * @brief 复位DMA模块
 */
void DMA_Reset(void)
{
    SYS_SoftResetModule(SYS_MODULE_DMA0);
}

/**
 * @brief 设置DMA通道源地址和目的地址
 *
 * @param DMAx DMA通道寄存器基地址
 * @param addr1 源地址
 * @param addr2 目的地址
 */
void DMA_SetAddr(DMA_TypeDef *DMAx, uint32_t addr1, uint32_t addr2)
{
    DMAx->SADR = addr1;
    DMAx->DADR = addr2;
}

/**
 * @brief 设置DMA通道传输长度
 *
 * @param DMAx DMA通道寄存器基地址
 * @param len 传输长度
 */
void DMA_SetLength(DMA_TypeDef *DMAx, uint32_t len)
{
    if ((DMAx == DMA1) || (DMAx == DMA2))
    {
        if (len >= 0x100)
        {
            len = 0xff;
        }
    }
    else if (len >= 0x1000)
    {
        len = 0xfff;
    }
    else
    {
        __NOP();
    }
    DMAx->CCR = DMAx->CCR & ~BIT0;
    do
    {
        DMAx->CTMS = len;
    } while (DMAx->CTMS != len);
    DMAx->CCR = DMAx->CCR | BIT0;
}

/**
 * @brief 触发DMA通道传输
 *
 * @param DMAx DMA通道寄存器基地址
 */
void DMA_Trigger(DMA_TypeDef *DMAx)
{
    REG_SET(DMAx->REN, DMA_REN_SW_TRIG);
}

/**
 * @brief 设置DMA通道请求源
 *
 * @param DMAx DMA通道寄存器基地址
 * @param DMA_REQ_x 请求源MASK
 */
void DMA_SetRequest(DMA_TypeDef *DMAx, uint32_t DMA_REQ_x)
{
    REG_SET(DMAx->REN, DMA_REQ_x);
}

/**
 * @brief 使能或禁用DMA通道
 *
 * @param DMAx DMA寄存器基地址
 * @param Channel_EN 通道使能MASK
 */
void DMA_CHx_EN(DMA_TypeDef *DMAx, uint32_t Channel_EN)
{
    if (Channel_EN)
    {
        DMAx->CCR |= 0x01; // 使能通道
    }
    else
    {
        DMAx->CCR &= ~0x01; // 禁用通道
    }
}

/**
 * @brief 使用dma实现的内存拷贝
 * @param DMAx DMA寄存器基地址
 * @param dest 目标内存指针
 * @param src 源内存指针
 * @param n 要复制的字节数，如果使用dma1或dma2，字节数应小于0xff，如果使用dma0或dma3，字节数小于0xffff
 */
void DMA_Memcpy(DMA_TypeDef *DMAx, void *dest, void *src, uint32_t n)
{
    uint32_t ifbit  = 0;
    uint32_t maxlen = 0;
    if (DMAx == DMA0)
    {
        ifbit  = BIT0;
        maxlen = 65535;
    }
    else if (DMAx == DMA1)
    {
        ifbit  = BIT1;
        maxlen = 255;
    }
    else if (DMAx == DMA2)
    {
        ifbit  = BIT2;
        maxlen = 255;
    }
    else if (DMAx == DMA3)
    {
        ifbit  = BIT3;
        maxlen = 65535;
    }
    else
    {
        __NOP();
    }
    if (n > maxlen)
    {
        n = maxlen;
    }
    else
    {
        __NOP();
    }
    DMA_Enable();
    DMA_InitTypeDef dmaconfig;
    DMA_StructInit(&dmaconfig);

    dmaconfig.IRQ_EN = 0;
    dmaconfig.SBTW   = 0;                        ///< 源地址访问位宽 0: Byte 1: Halfword 2: Word
    dmaconfig.DBTW   = 0;                        ///< 目的地址访问位宽 0: Byte 1: Halfword 2: Word
    dmaconfig.SINC   = 1;                        ///< 源地址递增使能
    dmaconfig.DINC   = 1;                        ///< 目的地址递增使能
    dmaconfig.CIRC   = 0;                        ///< 循环模式使能
    dmaconfig.RMODE  = 0;                        ///< 多轮传输使能，多个DMA 请求完成传输
    dmaconfig.EN     = 1;                        ///< 通道使能
    dmaconfig.TMS    = n;                        ///< DMA 通道 x 数据搬运次数。
    dmaconfig.SADR   = (uint32_t)(intptr_t)src;  ///< DMA 通道 x 源地址
    dmaconfig.DADR   = (uint32_t)(intptr_t)dest; ///< DMA 通道 x 目的地址
    dmaconfig.REN    = 0;                        ///< DMA 请求使能
    DMA_Init(DMAx, &dmaconfig);
    DMA0_IF   = ifbit;
    DMAx->REN = DMA_REN_SW_TRIG;
    while ((DMA0_IF & ifbit) == 0)
    {
        __NOP();
    }
}
/**
 * @brief 使用dma向特定寄存器写入多个数据，可用于crc计算等需要连续对单个寄存器写入数据的情况
 * @param DMAx DMA寄存器基地址
 * @param dest 目标内存指针
 * @param src 源内存指针
 * @param n 要复制的字节数，如果使用dma1或dma2，字节数应小于0xff，如果使用dma0或dma3，字节数小于0xffff
 * @param DMA_TRANS_x 位宽，输入输出位宽一致
 */
void DMA_Mem2Reg(DMA_TypeDef *DMAx, void *dest, void *src, uint32_t n, uint8_t DMA_TRANS_x)
{
    uint32_t ifbit  = 0;
    uint32_t maxlen = 0;
    if (DMAx == DMA0)
    {
        ifbit  = BIT0;
        maxlen = 65535;
    }
    else if (DMAx == DMA1)
    {
        ifbit  = BIT1;
        maxlen = 255;
    }
    else if (DMAx == DMA2)
    {
        ifbit  = BIT2;
        maxlen = 255;
    }
    else if (DMAx == DMA3)
    {
        ifbit  = BIT3;
        maxlen = 65535;
    }
    else
    {
        __NOP();
    }
    if (n > maxlen)
    {
        n = maxlen;
    }
    else
    {
        __NOP();
    }
    DMA_Enable();
    DMA_InitTypeDef dmaconfig;
    DMA_StructInit(&dmaconfig);

    dmaconfig.IRQ_EN = 0;
    dmaconfig.SBTW   = DMA_TRANS_x;              ///< 源地址访问位宽 0: Byte 1: Halfword 2: Word
    dmaconfig.DBTW   = DMA_TRANS_x;              ///< 目的地址访问位宽 0: Byte 1: Halfword 2: Word
    dmaconfig.SINC   = 1;                        ///< 源地址递增使能
    dmaconfig.DINC   = 0;                        ///< 目的地址递增使能
    dmaconfig.CIRC   = 0;                        ///< 循环模式使能
    dmaconfig.RMODE  = 0;                        ///< 多轮传输使能，多个DMA 请求完成传输
    dmaconfig.EN     = 1;                        ///< 通道使能
    dmaconfig.TMS    = n;                        ///< DMA 通道 x 数据搬运次数。
    dmaconfig.SADR   = (uint32_t)(intptr_t)src;  ///< DMA 通道 x 源地址
    dmaconfig.DADR   = (uint32_t)(intptr_t)dest; ///< DMA 通道 x 目的地址
    dmaconfig.REN    = 0;                        ///< DMA 请求使能
    DMA_Init(DMAx, &dmaconfig);
    DMA0_IF   = ifbit;
    DMAx->REN = DMA_REN_SW_TRIG;
    while ((DMA0_IF & ifbit) == 0)
    {
        __NOP();
    }
}
