/**
 * \file    drv_dma.c
 * \brief   DMA operations
 *
 * \par     Copyright (c) 2018 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2018-06-03
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2018-06-03  Original Creation
 * 
 */

#include "drv_dma.h"
#include "drv_chip.h"
#include "sync.h"
#include "drv_interrupt.h"

void *drv_dma_memcpy(void *dst, const void *src, u32_t count, DRV_DMA_MODE_t mode)
{
    if (!count)
        return(dst);

    sync_enter_critical();

    while(DRV_REG_GET(DMA0_CTRL_REG2) & DMA0_CTRL_REG2_DMA_BUSY_FLDSET);

    DRV_REG_SET(DMA0_CTRL_REG1, (u32_t)dst);
    DRV_REG_SET(DMA0_CTRL_REG0, (u32_t)src);
    
    if ((mode) && ((u32_t)dst > CHIP_REG_0)) // Memory address decrement
    {
        DRV_REG_SET(DMA0_CTRL_REG2, DMA0_CTRL_REG2_DMA_BUSY_FLDSET | ((mode << DMA0_CTRL_REG2_DMA_MODE) & DMA0_CTRL_REG2_DMA_MODE_FLDSET) | count);
    }
    else {   // Memory address increment
        DRV_REG_SET(DMA0_CTRL_REG2, DMA0_CTRL_REG2_DMA_BUSY_FLDSET | count);
    }

    while(DRV_REG_GET(DMA0_CTRL_REG2) & DMA0_CTRL_REG2_DMA_BUSY_FLDSET);

    sync_exit_critical();

    return(dst);
}

void *drv_dma_memmove(void *dst, const void *src, u32_t count)
{
    if (!count)
        return(dst);

    sync_enter_critical();

    while(DRV_REG_GET(DMA0_CTRL_REG2) & DMA0_CTRL_REG2_DMA_BUSY_FLDSET);

    if (((u32_t)src < (u32_t)dst) && ((u32_t)dst < ((u32_t)src + count)) && ((u32_t)dst > CHIP_REG_0))
    {
        /* Have to copy backwards */
        DRV_REG_SET(DMA0_CTRL_REG1, (u32_t)dst + count - 1);
        DRV_REG_SET(DMA0_CTRL_REG0, (u32_t)src + count - 1);
        DRV_REG_SET(DMA0_CTRL_REG2, DMA0_CTRL_REG2_DMA_BUSY_FLDSET | ((DRV_DMA_MODE_ADDR_DEC << DMA0_CTRL_REG2_DMA_MODE) & DMA0_CTRL_REG2_DMA_MODE_FLDSET) | count);
    }
    else
    {
        DRV_REG_SET(DMA0_CTRL_REG1, (u32_t)dst);
        DRV_REG_SET(DMA0_CTRL_REG0, (u32_t)src);
        DRV_REG_SET(DMA0_CTRL_REG2, DMA0_CTRL_REG2_DMA_BUSY_FLDSET | count);
    }

    while(DRV_REG_GET(DMA0_CTRL_REG2) & DMA0_CTRL_REG2_DMA_BUSY_FLDSET);

    sync_exit_critical();

    return(dst);
}


void drv_dma1_memcpy(void *dst, const void *src, u32_t count, DRV_DMA_MODE_t mode, bool_t intr, v_func_ptr_t intr_cb)
{
    if (!count)
        return;

    sync_enter_critical();

    DRV_REG_SET(DMA1_CTRL_REG1, (u32_t)dst);
    DRV_REG_SET(DMA1_CTRL_REG0, (u32_t)src);

    if (intr)
    {
        drv_intr_ext_set(IPIC_CFG_INTR_IDX_DMA_INT, TRUE, IPIC_INTR_PRI_7, intr_cb);
        DRV_REG_FIELD_SET(DMA_INT_REG, DMA1_INT_ENA, TRUE);
    }
    else {
        drv_intr_ext_set(IPIC_CFG_INTR_IDX_DMA_INT, FALSE, IPIC_INTR_PRI_7, intr_cb);
        DRV_REG_FIELD_SET(DMA_INT_REG, DMA1_INT_ENA, FALSE);
    }
    
    if ((mode) && ((u32_t)dst > CHIP_REG_0)) // Memory address decrement
    {
        if (DRV_DMA_MODE_SRC_ADDR_HOLD == mode)
        {
            // There is no need to set busy in SRC address hold mode
            DRV_REG_SET(DMA1_CTRL_REG2, ((mode << DMA1_CTRL_REG2_DMA_MODE) & DMA1_CTRL_REG2_DMA_MODE_FLDSET) | count);
        }
        else {
            DRV_REG_SET(DMA1_CTRL_REG2, DMA1_CTRL_REG2_DMA_BUSY_FLDSET | ((mode << DMA1_CTRL_REG2_DMA_MODE) & DMA1_CTRL_REG2_DMA_MODE_FLDSET) | count);
        }
    }
    else {   // Memory address increment
        DRV_REG_SET(DMA1_CTRL_REG2, DMA1_CTRL_REG2_DMA_BUSY_FLDSET | count);
    }

    if (!intr)
    {
        while(DRV_REG_GET(DMA1_CTRL_REG2) & DMA1_CTRL_REG2_DMA_BUSY_FLDSET);
    }

    sync_exit_critical();
}

void drv_dma1_memcpy2(void *dst, const void *src, u32_t count, DRV_DMA_MODE_t mode, bool_t intr, v_func_ptr_t intr_cb, u32_t arg)
{
    if (!count)
        return;

    sync_enter_critical();

    DRV_REG_SET(DMA1_CTRL_REG1, (u32_t)dst);
    DRV_REG_SET(DMA1_CTRL_REG0, (u32_t)src);

    if (intr)
    {
        drv_intr_ext_handler_arg_set(IPIC_CFG_INTR_IDX_DMA_INT, TRUE, IPIC_INTR_PRI_7, intr_cb, arg);
        DRV_REG_FIELD_SET(DMA_INT_REG, DMA1_INT_ENA, TRUE);
    }
    else {
        drv_intr_ext_handler_arg_set(IPIC_CFG_INTR_IDX_DMA_INT, FALSE, IPIC_INTR_PRI_7, intr_cb, arg);
        DRV_REG_FIELD_SET(DMA_INT_REG, DMA1_INT_ENA, FALSE);
    }
    
    if ((mode) && ((u32_t)dst > CHIP_REG_0)) // Memory address decrement
    {
        if (DRV_DMA_MODE_SRC_ADDR_HOLD == mode)
        {
            // There is no need to set busy in SRC address hold mode
            DRV_REG_SET(DMA1_CTRL_REG2, ((mode << DMA1_CTRL_REG2_DMA_MODE) & DMA1_CTRL_REG2_DMA_MODE_FLDSET) | count);
        }
        else {
            DRV_REG_SET(DMA1_CTRL_REG2, DMA1_CTRL_REG2_DMA_BUSY_FLDSET | ((mode << DMA1_CTRL_REG2_DMA_MODE) & DMA1_CTRL_REG2_DMA_MODE_FLDSET) | count);
        }
    }
    else {   // Memory address increment
        DRV_REG_SET(DMA1_CTRL_REG2, DMA1_CTRL_REG2_DMA_BUSY_FLDSET | count);
    }

    if (!intr)
    {
        while(DRV_REG_GET(DMA1_CTRL_REG2) & DMA1_CTRL_REG2_DMA_BUSY_FLDSET);
    }

    sync_exit_critical();
}


void drv_dma1_stop(void)
{
    u32_t dst = 0, src = 0;

    sync_enter_critical();

    DRV_REG_SET(DMA1_CTRL_REG2, 0);

    DRV_REG_SET(DMA1_CTRL_REG1, (u32_t)&dst);
    DRV_REG_SET(DMA1_CTRL_REG0, (u32_t)&src);

    drv_intr_ext_set(IPIC_CFG_INTR_IDX_DMA_INT, FALSE, IPIC_INTR_PRI_7, NULL);
    DRV_REG_FIELD_SET(DMA_INT_REG, DMA1_INT_ENA, FALSE);
    
    DRV_REG_SET(DMA1_CTRL_REG2, DMA1_CTRL_REG2_DMA_BUSY_FLDSET | (sizeof(u32_t)));

    while(DRV_REG_GET(DMA1_CTRL_REG2) & DMA1_CTRL_REG2_DMA_BUSY_FLDSET);

    sync_exit_critical();
}


