/*******************************************************************************
 * Copyright (c) 2018-2021 Renesas Electronics Corporation. All rights reserved.
 *
 *     RENESAS ELECTRONICS CONFIDENTIAL AND PROPRIETARY
 *
 * This software is provided as reference/sample code under the license
 * agreement between Renesas Electronics Corporation and licensee (the
 * "License Agreement") and shall be treated as specified in the License
 * Agreement.
 * These instructions, statements, and software are the confidential
 * information of Renesas Electronics Corporation. They must be used and
 * modified solely for the purpose for which it was furnished by Renesas
 * Electronics Corporation. All or part of these instructions, statements and
 * software must not be reproduced nor disclosed to any third party in any
 * form, unless permitted by the License Agreement.
 *
 * THIS SOFTWARE IS PROVIDED BY RENESAS ELEOCTRONICS CORPORATION "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * SATISFACTORY QUALITY, ACCURACY, TITLE AND NON-INFRINGEMENT ARE DISCLAIMED.
 * IN NO EVENT SHALL RENESAS ELECTRONICS CORPORATION BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 ******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : remap driver
 ******************************************************************************/
/******************************************************************************
 * @file          acc_prot.c
 * - Version      : 0.01
 * @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
 *         : 03.07.2020 0.01     First Release
 *****************************************************************************/

#include <stdint.h>
#include <remap.h>
#include <remap_register.h>
#include <mem_io.h>
#include <log.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    (uint8_t)(0xFFU)

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

/* Remap management table */
static st_remap_address_table_t s_remap_tbl[] = {
    [0] =   {REMAP_UNUSED,  0x00000000U},
    [1] =   {REMAP_UNUSED,  0x00000000U},
    [2] =   {REMAP_UNUSED,  0x00000000U},
    [3] =   {REMAP_UNUSED,  0x00000000U},
    [4] =   {REMAP_UNUSED,  0x00000000U},
    [5] =   {REMAP_UNUSED,  0x00000000U},
    [6] =   {6,             0xE7200000U},
    [7] =   {7,             0xE6400000U},
    [8] =   {8,             0xFFC10000U},
    [9] =   {9,             0xE6E00000U},
    [10] =  {10,            0xFFC10000U},
    [11] =  {11,            0xEE200000U},
    [12] =  {12,            0xE6200000U},
    [13] =  {13,            0xE6000000U},
    [14] =  {14,            0xE6600000U},
    [15] =  {15,            0xEB200000U},
};

static void remap_reg_write(uint32_t num, uint32_t value);

static 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(SICREMAP2M(num), value);
        mem_write32(SICREMAP2M(num), ~value);
        mem_write32(SICREMAP2M(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 size, uint32_t *remap_addr)
{
    uint32_t loop;
    uint32_t loop2;
    uint32_t map_num;
    uint32_t set_addr;

    /* size parameter check */
    if(size == 0U)
    {
        ERROR("Remap address size error.\n");
        panic;
    }

    /* Calculate the number to be registered in the remap space */
    map_num = (addr & REMAP_2M_MASK) + size;
    if((map_num % REMAP_2M_SIZE) == 0U)
    {
        map_num = (map_num / REMAP_2M_SIZE);
    }
    else
    {
        map_num = (map_num / REMAP_2M_SIZE) + 1U;
    }

    /* 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)
        {
            /* Judge whether it is possible to secure a necessary
               size for remapping in the unused area.       */
            for (loop2 = loop; loop2 < (map_num + loop); ++loop2)
            {
                if (REMAP_UNUSED != s_remap_tbl[loop2].number)
                {
                    break;
                }
            }
            if ((map_num + loop) <= loop2)
            {
                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;
    }

    __asm__ __volatile__ ("SYNCM");

    /* Set remap area */
    set_addr = addr & ~REMAP_2M_MASK;
    for (loop2 = loop; loop2 < (map_num + loop); ++loop2)
    {
        /* Update the table managing the remap space */
        s_remap_tbl[loop2].address = set_addr;
        s_remap_tbl[loop2].number = (uint8_t)loop;
        /* Set SICREMAP register */
        remap_reg_write(loop2, set_addr);
        set_addr += REMAP_2M_SIZE;
        INFO("s_remap_tbl[%d].number = 0x%x\n",loop,s_remap_tbl[loop2].number);
        INFO("s_remap_tbl[%d].address = 0x%x\n",loop,s_remap_tbl[loop2].address);
    }

    __asm__ __volatile__ ("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;
    uint32_t loop2;
    uint32_t remove_num;

    /* 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;
    }
    __asm__ __volatile__ ("SYNCM");

    /* release remap area */
    remove_num = s_remap_tbl[loop].number;
    for (loop2 = remove_num; loop2 < REMAP_TBL_MAX; ++loop2)
    {
        if (s_remap_tbl[loop2].number != remove_num)
        {
            break;
        }
        /* Update the table managing the remap space */
        s_remap_tbl[loop2].address = 0U;
        s_remap_tbl[loop2].number = REMAP_UNUSED;
        /* Release SICREMAP register */
        remap_reg_write(loop2, ICU_REMAP_CALC(loop2));
    }

    __asm__ __volatile__ ("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 = SICREMAP2M(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(SICREMAP2M(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) */
