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

/*******************************************************************************
 * DESCRIPTION   : Loader main common function
 ******************************************************************************/
/******************************************************************************
 * @file          loader_main_common.c
 * - Version      : 0.04
 * @brief         1.Output boot message.
 *                2.Judge boot mode.
 *                3.Set BL31 parameter.
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 02.04.2022 0.01     First Release
 *         : 04.04.2023 0.02     Removed stdio.h.
 *         : 21.08.2023 0.03     Add support for V4M.
 *         : 19.09.2023 0.04     Add log output for build option LSI.
 *****************************************************************************/

#include <stdint.h>
#include <stddef.h>  /* NULL pointer */
#include <mem_io.h>
#include <loader_main_common.h>
#include <rcar_def.h>
#include <rst_register.h>
#include <rom_api.h>
#include <log.h>
#include <remap.h>
#include <cpg.h>
#include <fcpr.h>
#include <access_protection.h>
#include <gpio.h>

#define RST_MODEMR0_MD5         (0x00000020U)

/* Define for Work Around of APMU */
#define CL0GRP3_BIT      (1U << 3)
#define CL1GRP3_BIT      (1U << 7)
#define RTGRP3_BIT       (1U << 19)
#define APMU_ACC_ENB_FOR_ARM_CPU    (CL0GRP3_BIT | CL1GRP3_BIT | RTGRP3_BIT)

static void judge_bootmode(uint32_t lcs);

/* global variable */
uint32_t is_verify = SECURE_BOOT;

static void judge_bootmode(uint32_t lcs)
{
    uint32_t md;

    /* LCS Status check */
    if((lcs != LCS_CM)
    && (lcs != LCS_DM)
    && (lcs != LCS_SD)
    && (lcs != LCS_SE)
    && (lcs != LCS_FA))
    {
        ERROR("LCS state error."
            "LCS = 0x%x", lcs);
        panic;
    }

    md = (mem_read32(RST_MODEMR0) & RST_MODEMR0_MD5) >> 5U;
    if (lcs == LCS_SE)
    {
        /* LCS=SE => Secure boot */
        is_verify = SECURE_BOOT;
    }
    else if ((lcs != LCS_SD) && (md == 0U))
    {
        /* LCS=CM/DM/FA and MD5=0 => Secure boot */
        is_verify = SECURE_BOOT;
    }
    else
    {
        /* LCS=SD or MD5=1 => Normal boot */
        is_verify = NORMAL_BOOT;
    }

    if (NORMAL_BOOT != is_verify)
    {
        NOTICE("Secure boot(ICUMX)\n");
    }
    else
    {
        NOTICE("Normal boot(ICUMX)\n");
    }
}
/* End of function judge_bootmode(uint32_t lcs) */

void print_boot_msg(void)
{
    uint32_t lcs = 0U;  /* store LCS state */
    uint32_t lcs_size = sizeof(lcs);
    uint32_t prr;
    uint32_t reg;       /* store register value */
    uint32_t ret;       /* store return value */
    __attribute__((__unused__)) const char *str;
    __attribute__((__unused__)) const char *product_s4      = "S4";
    __attribute__((__unused__)) const char *product_v4h     = "V4H";
    __attribute__((__unused__)) const char *product_v4m     = "V4M";
    __attribute__((__unused__)) const char *unknown         = "unknown";
    __attribute__((__unused__)) const char *boot_hyper80_160= "HyperFlash";
    __attribute__((__unused__)) const char *boot_serial40   = "Serial Flash";
    __attribute__((__unused__)) const char *boot_qspi80_160 = "Octal SPI Flash";
    __attribute__((__unused__)) const char *boot_emmc50x8   = "eMMC(50MHz x8)";
    const char *lcs_name[8U] = {
        [LCS_CM] = "CM",
        [LCS_DM] = "DM",
        [LCS_SD] = "SD",
        [LCS_SE] = "SE",
        [LCS_FA] = "FA",
    };

    NOTICE("ICUMX Loader Rev.%s\n", IPL_VERSION);

    NOTICE("LSI=%d (Build Option : S4=0, V4H=1, V4M=2)\n", RCAR_LSI);

    NOTICE("%s\n", build_message);
#if (BOOT_TIME_CHECK != 0)
    gpio_N1305(2);
#endif
    /* Get PRR */
    prr = mem_read32(PRR);
    prr &= (PRR_PRODUCT_MASK | PRR_MAJOR_MASK | PRR_MINOR_MASK);
    switch (prr & PRR_PRODUCT_MASK)
    {
        case PRR_PRODUCT_S4:
        {
            str = product_s4;
            break;
        }
        case PRR_PRODUCT_V4H:
        {
            str = product_v4h;
            break;
        }
        case PRR_PRODUCT_V4M:
        {
            str = product_v4m;
            break;
        }
        default:
        {
            str = unknown;
            break;
        }
    }
    NOTICE("PRR is R-Car %s Ver%d.%d\n", str,
        ((prr & PRR_MAJOR_MASK) >> PRR_MAJOR_SHIFT) + PRR_MAJOR_OFFSET,
         (prr & PRR_MINOR_MASK));

    reg = mem_read32(RST_MODEMR0);
    reg &= RST_MODEMR0_BOOT_DEV_MASK;

    switch (reg)
    {
        case RST_MODEMR0_BOOT_DEV_HYPERFLASH160:
        case RST_MODEMR0_BOOT_DEV_HYPERFLASH80:
        {
            str = boot_hyper80_160;
            break;
        }
        case RST_MODEMR0_BOOT_DEV_SERIAL_FLASH40:
        case RST_MODEMR0_BOOT_DEV_SERIAL_FLASH:
        {
            str = boot_serial40;
            break;
        }
        case RST_MODEMR0_BOOT_DEV_QSPI_FLASH80:
        {
            str = boot_qspi80_160;
            break;
        }
        case RST_MODEMR0_BOOT_DEV_EMMC_50X8:
        {
            str = boot_emmc50x8;
            break;
        }
        default:
        {
            str = unknown;
            break;
        }
    }
    NOTICE("Boot device is %s(0x%x)\n", str, reg);
#if (QSPI_DDR_MODE==1)
    NOTICE("Change QSPI DDR Transfer mode\n");
#endif

    /* Get LCS state */
    ret = call_ROM_GetLcs(&lcs, lcs_size);
    str = unknown;
    if (ret == ROMAPI_OK)
    {
        if (NULL != lcs_name[lcs])
        {
            str = lcs_name[lcs];
        }
    }
    NOTICE("LCM state is %s\n", str);

    /* If PRR is S4 Ver.1.0 */
    if ((PRR_PRODUCT_S4 == (prr & PRR_PRODUCT_MASK)) && (0U == (prr & PRR_CUT_MASK)))
    {
        set_sicremap_s4v10();   /* downgrade SICREMAP setting. */
        is_verify = NORMAL_BOOT;/* S4 Ver.1.0 is Normal Boot only. */
    }
    else
    {
        /* LCS judgement for secure boot */
        judge_bootmode(lcs);
    }

#if (SET_FCPR_PARAM == FCPR_ENABLE)
    NOTICE("Data Compression Enable\n");
    NOTICE("Start address = 0x%08x End address = 0x%08x\n", COMPRESSION_START_ADDR, COMPRESSION_END_ADDR);
#endif

#if (ACC_PROT_ENABLE == PROTECTION_ENABLE)
    NOTICE("Access protection Enable\n");
#endif
}
/* End of function print_boot_msg(void) */

/* Work Around setting for APMU */
void wa_setting_apmu(void)
{
    uint32_t apmu_tmp;

    /* Work Around setting for D0_ACCENR-DOMAIN3_ACCENR */
    apmu_tmp = mem_read32(APMU_D0_ACCENR);
    apmu_tmp |= APMU_ACC_ENB_FOR_ARM_CPU;
    apmu_reg_write(APMU_D0_ACCENR, apmu_tmp);

    apmu_tmp = mem_read32(APMU_D1_ACCENR);
    apmu_tmp |= APMU_ACC_ENB_FOR_ARM_CPU;
    apmu_reg_write(APMU_D1_ACCENR, apmu_tmp);

    apmu_tmp = mem_read32(APMU_D2_ACCENR);
    apmu_tmp |= APMU_ACC_ENB_FOR_ARM_CPU;
    apmu_reg_write(APMU_D2_ACCENR, apmu_tmp);

    apmu_tmp = mem_read32(APMU_D3_ACCENR);
    apmu_tmp |= APMU_ACC_ENB_FOR_ARM_CPU;
    apmu_reg_write(APMU_D3_ACCENR, apmu_tmp);
}
/* End of function wa_setting_apmu(void) */
