/*******************************************************************************
 * 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-2023 Renesas Electronics Corporation All rights reserved.
 *******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : remap driver
 ******************************************************************************/
/******************************************************************************
 * @file          remap.c
 * - Version      : 0.06
 * @brief         1. Setting of SIC REMAP AREA.
 *                2. Release of SIC REMAP AREA.
 *                3. Calculation of logical address.
 *                4. Calculation of physical address.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 28.07.2021 0.01     First Release
 *         : 03.09.2021 0.02     Modify remap_reg_write function to inline.
 *         : 06.01.2022 0.03     Static analysis support
 *         : 23.05.2022 0.04     Supported SICREMAP address for S4 ver1.0
 *                               S4/V4H differences applied to s_remap_tbl.
 *         : 15.05.2023 0.05     Add get_cfremap_addr() function to jump
 *                               the secure firmware on CF remap Area.
 *         : 21.08.2023 0.06     Add support for V4M.
 *****************************************************************************/

#include <stdint.h>
#include <remap.h>
#include <remap_register.h>
#include <mem_io.h>
#include <log.h>
#include <inline_asm.h>

/* Range of SICREMAP2M register */
#define REMAP_REG_MAX   (16U)

#define REMAP_2M_BITS   (21U)
#define REMAP_2M_SIZE   ((uint32_t)1U << REMAP_2M_BITS)
#define REMAP_2M_MASK   (REMAP_2M_SIZE - 1U)
#define REMAP_TBL_MAX   (sizeof(s_remap_tbl)/sizeof(s_remap_tbl[0]))
#define REMAP_UNUSED    (0xFFFFFFFFU)

typedef struct {
    uint32_t number;
    uint32_t address;
}st_remap_address_table_t;

/* Remap management table */
static st_remap_address_table_t s_remap_tbl[REMAP_REG_MAX] = {
    [0] =   {REMAP_UNUSED,  0x00000000U},
    [1] =   {REMAP_UNUSED,  0x00000000U},
    [2] =   {REMAP_UNUSED,  0x00000000U},
    [3] =   {REMAP_UNUSED,  0x00000000U},
#if (RCAR_LSI == RCAR_S4)
    [4] =   {4,             0xD8E00000},    /* MCU */
#elif ((RCAR_LSI == RCAR_V4H) || (RCAR_LSI == RCAR_V4M))
    [4] =   {REMAP_UNUSED,  0x00000000U},
#endif
    [5] =   {5,             0xE7600000U},   /* Region ID */
    [6] =   {6,             0xE7200000U},   /* SYS-DMAC0 */
    [7] =   {7,             0xE6400000U},   /* HSCIF */
    [8] =   {8,             0xEE000000U},   /* MMC */
    [9] =   {9,             0xE6E00000U},   /* SCIF */
    [10] =  {10,            0xFFC00000U},   /* RT-DMAC0,PFC(MCU) */
    [11] =  {11,            0xEE200000U},   /* RPC */
    [12] =  {12,            0xE6200000U},   /* ECM,AP-System Core */
    [13] =  {13,            0xE6000000U},   /* PFC,GPIO,CPGA,RESET */
    [14] =  {14,            0xE6600000U},   /* CC63S,AXMM,QoS,FCPR */
    [15] =  {15,            0xEB200000U},   /* RT-SRAM */
};

static inline uint32_t get_sicremap2m_addr(uint32_t num)
{
    return (REMAP_BASE + (num * 0x0004U));
}
/* End of function get_sicremap2m_addr(uint32_t num) */

static inline void remap_reg_write(uint32_t num, uint32_t value)
{
    /* Specific write Procedure for Write-Protected Register. */
    do
    {
        mem_write32(ICUMX_PROT0PCMD, PROTCMD_START);
        mem_write32(get_sicremap2m_addr(num), value);
        mem_write32(get_sicremap2m_addr(num), ~value);
        mem_write32(get_sicremap2m_addr(num), value);
    } while (mem_read32(ICUMX_PROT0PS) == PROTS0ERR);
}
/* End of function remap_reg_write(uint32_t num, uint32_t value) */

void remap_register(uint32_t addr, uint32_t *remap_addr)
{
    uint32_t loop;
    uint32_t set_addr;

    /* Check unused area in order from the top of
       the remap management table.          */
    for (loop = 0U; loop < REMAP_TBL_MAX; ++loop)
    {
        if (REMAP_UNUSED == s_remap_tbl[loop].number)
        {
            break;
        }
    }

    /* When necessary area can not be secured */
    if (REMAP_TBL_MAX <= loop)
    {
        ERROR("There is no space in the logical address area.\n");
        panic;
    }

    syncm();

    /* Set remap area */
    set_addr = addr & ~REMAP_2M_MASK;

    /* Update the table managing the remap space */
    s_remap_tbl[loop].address = set_addr;
    s_remap_tbl[loop].number = (uint8_t)loop;
    /* Set SICREMAP register */
    remap_reg_write(loop, set_addr);
    INFO("s_remap_tbl[%d].number = 0x%x\n",loop,s_remap_tbl[loop].number);
    INFO("s_remap_tbl[%d].address = 0x%x\n",loop,s_remap_tbl[loop].address);

    syncm();

    /* Calculating the logical address of the
       address received as an argument  */
    *remap_addr = icu_remap_calc(loop);
    *remap_addr += addr & REMAP_2M_MASK;
}
/* End of function remap_register(uint32_t addr, uint32_t size, uint32_t *remap_addr) */

void remap_unregister(uint32_t remap_addr)
{
    uint32_t loop;

    /* Is the remap space where the address of the argument is used? */
    for (loop = 0U; loop < REMAP_TBL_MAX; ++loop)
    {
        INFO("s_remap_tbl[%d].number = 0x%x\n",loop,s_remap_tbl[loop].number);
        if ((REMAP_UNUSED != s_remap_tbl[loop].number)
            && ((icu_remap_calc(loop) <= remap_addr)
                && (remap_addr < icu_remap_calc(loop + 1U))))
        {
            break;
        }
    }

    /* When an area to release can not be found */
    if (REMAP_TBL_MAX <= loop)
    {
        ERROR("Not registered in the logical address.\n"
            "remap address = 0x%x\n",remap_addr);
        panic;
    }
    syncm();

    /* release remap area */
    /* Update the table managing the remap space */
    s_remap_tbl[loop].address = 0U;
    s_remap_tbl[loop].number = REMAP_UNUSED;
    /* Release SICREMAP register */
    remap_reg_write(loop, icu_remap_calc(loop));

    syncm();
}
/* End of function remap_unregister(uint32_t remap_addr) */

uint32_t remap_get_phys_addr(uint32_t remap_addr)
{
    uint32_t phys_addr;
    uint32_t reg;

    /* It checks whether the argument is within the range
       of the logical address.              */
    if ((ICU_REMAP0 > remap_addr)
        || (icu_remap_calc(REMAP_REG_MAX) <= remap_addr))
    {
        ERROR("Address convert error.\n"
            "source address = 0x%x\n",remap_addr);
        panic;
    }

    /* Calculate the physical address of the argument */
    phys_addr = remap_addr - ICU_REMAP0;
    phys_addr >>= REMAP_2M_BITS;
    reg = get_sicremap2m_addr(phys_addr);
    reg = mem_read32(reg);
    phys_addr = reg + (remap_addr & REMAP_2M_MASK);

    return phys_addr;
}
/* End of function remap_get_phys_addr(uint32_t remap_addr) */

uint32_t remap_get_remap_addr(uint32_t phys_addr)
{
    uint32_t remap_addr;
    uint32_t reg;
    uint32_t loop;

    /* It checks whether the argument is within the range
       of the physical address registered in SICREMAP.  */
    for (loop = 0U; loop < REMAP_REG_MAX; loop++)
    {
        reg = mem_read32(get_sicremap2m_addr(loop));
        if ((reg <= phys_addr)
            && (phys_addr <= (reg + REMAP_2M_MASK)))
        {
            break;
        }
    }

    /* argument value is not used in the remap area. */
    if (REMAP_REG_MAX <= loop)
    {
        ERROR("Address convert error.\n"
            "source address = 0x%x\n",phys_addr);
        panic;
    }

    /* Calculate the logical address of the argument */
    remap_addr = icu_remap_calc(loop);
    remap_addr += phys_addr - reg;

    return remap_addr;
}
/* End of function remap_get_remap_addr(uint32_t phys_addr) */

uint32_t get_cfremap_addr(uint32_t fetch_addr)
{
    uint32_t cf_remap_addr   = 0x0U;
    uint32_t cf_current_base = mem_read32(ICUMX_CFREMAP);

    /* Get current setting of "physical address for cf remap base",
                    and calculate cf remap address of target fetch  */
    cf_remap_addr = fetch_addr - cf_current_base;

    /* Check whether the calculated address is outside of Code Fetch area */
    if ((CFREMAP_AREA_SIZE <= cf_remap_addr) || (fetch_addr < cf_current_base))
    {
        ERROR("Target fetch address is invalid: 0x%08x\n", fetch_addr);
        panic;
    }

    return cf_remap_addr;
}

void set_sicremap_s4v10(void)
{
#if (RCAR_LSI == RCAR_S4)
    /* Change the setting of SICREMAP for S4 Ver1.0 to be the setting of SICREMAP for S4 Ver1.1. */
    remap_reg_write(ICU_REMAP_NUM_RGID, ICU_REMAP_RGID);    /* SIC REMAP5:Region ID */
    remap_reg_write(ICU_REMAP_NUM_MCU , ICU_REMAP_MCU);     /* SIC REMAP4:MCU */
#endif
}
/* End of function set_sicremap_s4v10(void) */

void set_sicremap_fcpr(void)
{
    remap_reg_write(ICU_REMAP_NUM_FCPR, ICU_REMAP_FCPR);    /* SIC REMAP14:FCPR */
}
/* End of function set_sicremap_fcpr(void) */

