/*******************************************************************************
 * DISCLAIMER
 * This software is supplied by Renesas Electronics Corporation and is only
 * intended for use with Renesas products. No other uses are authorized. This
 * software is owned by Renesas Electronics Corporation and is protected under
 * all applicable laws, including copyright laws.
 * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
 * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
 * LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
 * TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
 * ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
 * FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
 * ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
 * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * Renesas reserves the right, without notice, to make changes to this software
 * and to discontinue the availability of this software. By using this software,
 * you agree to the additional terms and conditions found by accessing the
 * following link:
 * http://www.renesas.com/disclaimer
 * Copyright 2021-2022 Renesas Electronics Corporation All rights reserved.
 *******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : DMA driver
 ******************************************************************************/
/******************************************************************************
 * @file          dma.c
 * - Version      : 0.07
 * @brief         DMA driver.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 28.07.2021 0.01     First Release
 *         : 30.09.2021 0.02     Change function name load_start and
 *                               load_end function.
 *         : 15.10.2021 0.03     modified to not use CHCLR.
 *         : 06.01.2022 0.04     Add exception handling for ICUMX_WDTA.
 *         : 02.02.2022 0.05     Add MFIS Lock/Unlock.
 *         : 22.06.2022 0.06     Replace address align check to function.
 *         :                     Remove some defines.
 *         : 20.12.2022 0.07     Add mask when writing to TCR register.
 *****************************************************************************/

#include <stdint.h>
#include <mem_io.h>
#include <dma.h>
#include <rpc.h>
#include <log.h>
#include <wdt.h>
#include <mfis.h>

#define DMOR_INIT                   (uint16_t)(0x0301U)
#define DMOR_HW_INIT                (uint16_t)(0x0000U)
#define TCR_CNT_SHIFT               (6U)
#define TCR_UPPER8BIT_MASK          (0x00FFFFFFU)
#define CHCR_TRN_MODE               (0x00105409U)
#define CHCR_TE_BIT                 (0x00000002U)
#define TE_FLAG                     (0x00000000U)
#define CHCR_CAE_BIT                (0x80000000U)
#define CHCR_CAE_BIT_NOERROR        (0x00000000U)
#define CHCR_CAIE_BIT               (0x40000000U)
#define CHCR_DPM_BIT                (0x30000000U)
#define CHCR_RPT_BIT                (0x0F000000U)
#define CHCR_WAIT_BIT               (0x00800000U)
#define CHCR_DPB_BIT                (0x00400000U)
#define CHCR_DSE_BIT                (0x00080000U)
#define CHCR_DSIE_BIT               (0x00040000U)
#define CHCR_DM_BIT                 (0x0000C000U)
#define CHCR_SM_BIT                 (0x00003000U)
#define CHCR_RS_BIT                 (0x00000F00U)
#define CHCR_TS_BIT                 (0x00300018U)
#define CHCR_IE_BIT                 (0x00000004U)
#define CHCR_TE_BIT                 (0x00000002U)
#define CHCR_DE_BIT                 (0x00000001U)
#define CHCR_CONF_MASK              (CHCR_TS_BIT | CHCR_DM_BIT | CHCR_SM_BIT | CHCR_RS_BIT | CHCR_DE_BIT)
#define CHCR_DESCRIPTOR_CONF_MASK   (CHCR_DPM_BIT | CHCR_RPT_BIT | CHCR_WAIT_BIT | CHCR_DPB_BIT)
#define CHCR_INTERRUPT_MASK         (CHCR_CAIE_BIT | CHCR_DSIE_BIT | CHCR_IE_BIT)
#define CHCR_FLAG_MASK              (CHCR_CAE_BIT | CHCR_DSE_BIT | CHCR_TE_BIT)
#define CHCR_ALL_BIT_MASK           (CHCR_CONF_MASK | CHCR_DESCRIPTOR_CONF_MASK | CHCR_INTERRUPT_MASK | CHCR_FLAG_MASK)
#define DAR_HW_INIT                 (0x00000000U)
#define SAR_HW_INIT                 (0x00000000U)
#define TCR_HW_INIT                 (0x00000000U)

/* fraction mask for 256-byte units */
#define FRACTION_MASK_256_BYTE  (0x000000FFU)

void dma_init(void)
{
    uint32_t reg;

    /* DMA operation */
    mem_write16(RTDMA_DMOR, DMOR_INIT);
    /* DMA secure control register */
    reg = mem_read32(RTDMA_DMSEC);
    reg |= ((uint32_t)1U << DMACH);
    mem_write32(RTDMA_DMSEC, reg);

    reg = mem_read32(dma_get_rtdma_chcr_addr(DMACH));
    reg &= ~(CHCR_ALL_BIT_MASK);
    mem_write32(dma_get_rtdma_chcr_addr(DMACH), reg);
}
/* End of function dma_init(void) */

void dma_trans_start(uint32_t dst, uint32_t src, uint32_t len)
{
    uint32_t reg;

    /* dst and src must be 64-byte boundary. */
    dma_address_align_check(dst, src);

    /* round up 256 byte alignment */
    len += FRACTION_MASK_256_BYTE;
    len &= (~(uint32_t)(FRACTION_MASK_256_BYTE));

    /* DMA destination address */
    mem_write32(dma_get_rtdma_dar_addr(DMACH), dst);
    /* DMA source address */
    mem_write32(dma_get_rtdma_sar_addr(DMACH), src);
    /* DMA 64bytes-unit transfer  count */
    mem_write32(dma_get_rtdma_tcr_addr(DMACH), ((len >> TCR_CNT_SHIFT) & TCR_UPPER8BIT_MASK));
    /* Lock to avoid conflict with RPC */
    mfis_lock();
    /* DMA channel control */
    reg = mem_read32(dma_get_rtdma_chcr_addr(DMACH));
    reg |= CHCR_TRN_MODE;
    mem_write32(dma_get_rtdma_chcr_addr(DMACH), reg);
}
/* End of function dma_trans_start(uint32_t dst, uint32_t src, uint32_t len) */

void dma_trans_end_check(void)
{
    uint32_t reg;

    /* Check end of DMA transfer. */
    do
    {
        wdt_restart();
        reg = mem_read32(dma_get_rtdma_chcr_addr(DMACH));
        /* Check error of DMA transfer */
        if ((reg & CHCR_CAE_BIT) != CHCR_CAE_BIT_NOERROR)
        {
            ERROR("DMA - Channel Address Error\n");
            panic;
        }
    } while ((reg & CHCR_TE_BIT) == TE_FLAG);

    reg = mem_read32(dma_get_rtdma_chcr_addr(DMACH));
    reg &= ~(CHCR_ALL_BIT_MASK);
    mem_write32(dma_get_rtdma_chcr_addr(DMACH), reg);

    rpc_end_state_check();
    /* Unlock to avoid conflict with RPC */
    mfis_unlock();
}
/* End of function dma_trans_end_check(void) */

void dma_release(void)
{
    uint32_t reg;

    /* DMA channel control */
    reg = mem_read32(dma_get_rtdma_chcr_addr(DMACH));
    reg &= ~(CHCR_ALL_BIT_MASK);
    mem_write32(dma_get_rtdma_chcr_addr(DMACH), reg);

    /* DMA destination address */
    mem_write32(dma_get_rtdma_dar_addr(DMACH), DAR_HW_INIT);
    /* DMA source address */
    mem_write32(dma_get_rtdma_sar_addr(DMACH), SAR_HW_INIT);
    /* DMA 64bytes-unit transfer  count */
    mem_write32(dma_get_rtdma_tcr_addr(DMACH), TCR_HW_INIT);

    reg = mem_read32(RTDMA_DMSEC);
    reg &= (~((uint32_t)1U << DMACH));
    mem_write32(RTDMA_DMSEC, reg);

    /* DMA operation */
    mem_write16(RTDMA_DMOR, DMOR_HW_INIT);
}
/* End of function dma_release(void) */
