/*
 * Copyright (c) 2015-2021, Renesas Electronics Corporation
 * All rights reserved.
 */

/******************************************************************************
 * @file          dma_driver.c
 * - Version      : 0.01
 * @brief         DMA driver.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 29.05.2020 0.01     First Release
 *****************************************************************************/

#include <stdint.h>
#include <string.h>
#include <mmio.h>
#include "cpg_register.h"
#include "cpg_init.h"
#include "dma_register.h"
#include "dma_driver.h"
#include "debug.h"

static void enable_dma(void);
static void setup_dma(void);
static void start_dma(uint32_t dst, uint32_t src, uint32_t len);
static void end_dma(void);

static void enable_dma(void)
{
    /* Is the clock supply to the CPG disabled ? */
    while ((mmio_read_32(CPG_MSTPSR0) & RT_DMAC_BIT) != 0U)
    {
        /* Enables the clock supply to the CPG. */
        cpg_write(CPG_RMSTPCR0,
            mmio_read_32(CPG_RMSTPCR0) & (~RT_DMAC_BIT));
    }
}
/* End of function enable_dma */

static void setup_dma(void)
{
    /* DMA channel clear */
    mmio_write_32(DMA_RDMCHCLR, (1U << DMACH));
}
/* End of function setup_dma */

static void start_dma(uint32_t dst, uint32_t src, uint32_t len)
{

    uint32_t reg;

    /* DMA operation */
    mmio_write_16(DMA_RDMOR, 0x0301U);
    /* DMA secure control register */
    reg = mmio_read_32(DMA_RDMSEC);
    reg |= (1U << DMACH);
    mmio_write_32(DMA_RDMSEC, reg);
    /* DMA destination address */
    mmio_write_32(DMA_RDMDAR, dst);
    /* DMA source address */
    mmio_write_32(DMA_RDMSAR, src);
    /* DMA 64bytes-unit transfer  count */
    mmio_write_32(DMA_RDMTCR, (len >> 6U));
    /* DMA channel control */
    mmio_write_32(DMA_RDMCHCR, 0x00105409U);
}
/* End of function start_dma */

static void end_dma(void)
{
    uint32_t reg;

    /* DMA channel control */
    while ((mmio_read_32(DMA_RDMCHCR) & 0x00000002U) == 0x00000000U)
    {
        /* DMA channel control 32 */
        if ((mmio_read_32(DMA_RDMCHCR) & 0x80000000U) != 0U)
        {
            ERROR("DMA - Channel Address Error\n");
            panic();
            break;
        }
    }
    /* DMA channel clear 2 */
    mmio_write_32(DMA_RDMCHCLR,(1U << DMACH));
    reg = mmio_read_32(DMA_RDMSEC);
    reg &= (~(1U << DMACH));
    mmio_write_32(DMA_RDMSEC, reg);
}
/* End of function end_dma */

void init_dma(void)
{
    enable_dma();
    setup_dma();
}
/* End of function init_dma */

void exec_dma(uint32_t dst, uint32_t src, uint32_t len)
{
    uint32_t dmalen;
    uint32_t memlen;

    if (((dst & 0x3FU) != 0U) || ((src & 0x3FU) != 0U) ||
        ((len & 0xC0000000U) != 0U))
    {
        /* dst or src are not 64-bit alignment. */
        dmalen = 0U;
        memlen = len;
    }
    else
    {
        /* dst and src are 64-bit alignment. */
        dmalen = len & 0x3FFFFFC0U;
        memlen = len & 0x3FU;
    }
    if (dmalen != 0U)
    {
        start_dma(dst, src, dmalen);
        end_dma();
        dst += dmalen;
        src += dmalen;
    }
    else
    {
        if (memlen == 0U)
        {
            start_dma(dst, src, 0U);
            end_dma();
            dmalen = 0x01000000U << 6;
            dst += dmalen;
            src += dmalen;
        }
    }
    if (memlen != 0U)
    {
        (void)memcpy((void*)(uintptr_t)dst,
            (const void*)(uintptr_t)src,
                (size_t)memlen);
    }
}
/* End of function exec_dma */

