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

/*******************************************************************************
 * DESCRIPTION   : Loader main function for V4H
 ******************************************************************************/
/******************************************************************************
 * @file          loader_main.c
 * - Version      : 0.14
 * @brief         1. IP initialization.
 *                2. DMA transfer the binary image from Flash to RAM.
 *                3. Authentication of the transferred image.
 *                4. Boot CR and CA core.
 *                5. Release of used resources.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 28.07.2021 0.01     First Release
 *         : 03.09.2021 0.02     Modify macro definition name.
 *                               Add process to call rpc_release function.
 *         : 30.09.2021 0.03     Support of eMMC boot.
 *         : (30.11.2021 0.04)   Support boot sequence of V4H.
 *         : 03.12.2021 0.05     Add RT-VRAM extend mode.
 *         : 23.05.2022 0.06     Integration of S4 and V4H
 *                               Renamed from loader_main.c to loader_main_v4h.c.
 *         : 05.08.2022 0.07     Add authentication of software minimum
 *                               version table.
 *         : 30.09.2022 0.08     Modify authentication process of software
 *                               minimum version.
 *         : 20.01.2023 0.09     Add W/A OTLINT-5556: increased latency
 *         : 17.04.2023 0.10     Add setting for V4H v2.x leak current reduce
 *                               Removed stdio.h.
 *                               Add certificate authentication functions to be
 *                               execute before image transfer.
 *         : 15.05.2023 0.11     Change the jump address of secure firmware from
 *                               on SIC remap area to CF remap area.
 *         : 13.06.2024 0.12     Updated ECM register setting in wa_for_v4h2
 *                               function.
 *         : 27.12.2024 0.13     Change the location of ECC and EDC
 *                               initialization function.
 *         : 07.04.2025 0.14     Added ecm_init_setting function calling.
 *****************************************************************************/

#include <stdint.h>
#include <stdbool.h>
#include <loader_main.h>
#include <loader_main_common.h>
#include <cpu_on.h>
#include <image_load.h>
#include <ip_control.h>
#include <qos.h>
#include <rcar_def.h>
#include <remap.h>
#include <remap_register.h>
#include <rom_api.h>
#include <mem_io.h>
#include <log.h>
#include <wdt.h>
#include <micro_wait.h>
#include <rtvram.h>
#include <ram_def.h>
#include <access_protection.h>
#include "rcar_def.h"
#if (ECM_ERROR_ENABLE == 1)
#include "../ip/ddr/v4h/lpddr5/ecm_enable_v4h.h"
#endif /* ECM_ERROR_ENABLE == 1 */

/* Provided code */
#include "../ip/ddr/boot_init_dram.h"
#include "../ip/ddr/v4h/lpddr5/ecc_enable_v4h.h"
#include <cpg_register.h>
#include <san.h>
#include <gpio.h>

/* Time analysis */
#include <scmt.h>
#include <scmt_checkpoint.h>
#if (0 == (MEASURE_TIME))
  #define store_time_checkpoint(x,y)
  #define print_time_checkpoints(x)
#endif
#include <android_ab.h>

#define ICISTP_BASE    (BASE_ICISTP_ADDR)
#define ICISTPSTPER000 (ICISTP_BASE + 0x30300U)

#define EDC_BASE       (BASE_ECM_ADDR)
#define EDCSTRT20      (EDC_BASE + 0x80C0U)
#define TIDSTRT20      (EDC_BASE + 0x81C0U)

/* SYSC register */
#define SYSC_BASE       (BASE_SYSC_ADDR)
#define SYSC_SYSCSR     (SYSC_BASE + 0x0000U)   /* SYSC Status Register */
#define SYSC_SYSCISCR1  (SYSC_BASE + 0x0814U)   /* Interrupt Status/Clear Register1 */
#define SYSC_SYSCIER1   (SYSC_BASE + 0x0824U)   /* Interrupt Enable Register1 */
#define SYSC_SYSCIMR1   (SYSC_BASE + 0x0834U)   /* Interrupt MASK Register1 */

/* Power Domain Register */
#define SYSC_PDR41      (0x0A40U)   /* A1CNN0 */
#define SYSC_PDR42      (0x0A80U)   /* A2CN0 */
#define SYSC_PDR43      (0x0AC0U)   /* A3IR */
#define SYSC_PDRSR      (SYSC_BASE + 0x1000U)   /* Base address of Power Domain Status Register */
#define SYSC_PDRSR41    (SYSC_PDRSR + (SYSC_PDR41))
#define SYSC_PDRSR42    (SYSC_PDRSR + (SYSC_PDR42))
#define SYSC_PDRSR43    (SYSC_PDRSR + (SYSC_PDR43))
#define SYSC_PDRONCR    (SYSC_BASE + 0x1004U)   /* Base address of Power Domain Power-ON Control Register */
#define SYSC_PDRONCR41  (SYSC_PDRONCR + (SYSC_PDR41))
#define SYSC_PDRONCR42  (SYSC_PDRONCR + (SYSC_PDR42))
#define SYSC_PDRONCR43  (SYSC_PDRONCR + (SYSC_PDR43))

#define PDR_MAX         (3U)                /* Number of PDR's to be set */
#define SYSC_BIT_PDR41  (0x00000200U)       /* SYSC register target PDR41 bit */
#define SYSC_BIT_PDR42  (0x00000400U)       /* SYSC register target PDR42 bit */
#define SYSC_BIT_PDR43  (0x00000800U)       /* SYSC register target PDR43 bit */

#define SYSCSR_BUSY_MASK    (0x00000003U)   /* SYSC Power On or Power Off seaquence status */
#define SYSCSR_NOT_BUSY     (0x00000003U)   /* Not processing */

#define SYSC_PDRSR_PWR_MASK (0x00001111U)   /* PDR Power On / Off Status MASK */
#define SYSC_PDRSR_PWROFF   (0x00000001U)   /* PDR Power OFF Status */
#define SYSC_PDRONCR_PWRON  (0x00000001U)   /* PDR Power On request */
#define SYSC_PDR_PWR_PROC   (0x00000000U)   /* PDR Power On or Poweer off processing */

#define RGID_SET_RGID_FIN_FLG_ADDR  (0xFD95EFFCU)  /* 0xE635EFFC:Remap 12 */
#define RGID_SET_RGID_FIN_FLG_VAL   (0x64U)


#if (ADD_HOTPLUG_MAGIC == ADD_MAGIC_NUMBER)
#define HOTPLUG_MAGIC_NUM       (0x853F912EU)
#else
#define HOTPLUG_MAGIC_NUM       (0x00000000U)
#endif
__attribute__ ((section (".cr_hot_plug_magic"))) const uint32_t magic_num[4] = {0x00000000U, 0x00000000U,
                                                                                0x00000000U, HOTPLUG_MAGIC_NUM};

/* Prototype functions */
#include <san.h>
static void wa_for_v4h2(void);
static void set_leak_current_reduce_for_v4h2x(void);

uint32_t loader_main(void)
{
    uint32_t boot_ca_id;
    uint32_t auth_count = 0U;
    uint32_t boot_cpu;
    uint32_t tmp;
    __attribute__((__unused__)) uint32_t loop = 0U;
    __attribute__((__unused__)) uint32_t ca_load_num; /* number of load for Optional CA program */
    LOAD_INFO li[MAX_PLACED] = {0U};

#if (1 == (MEASURE_TIME))
    scmt_module_start();
    store_time_checkpoint("loader_main", 0);
#endif
#if (SAN_ENABLE == 1)
    /* Appendix A    Sequence of Activation
       step (5) Stop RWDT and System WDT */
    rwdt_init(0);
    swdt_init(0);
#endif
/*****************************************************************************
 *  Initialize Hardware
 *****************************************************************************/
    /* IP initialize, step(6) of Sequence of Activation? */
    ip_init();

#if (ECM_ERROR_ENABLE == 1)
    ecm_init_setting();
#endif /* ECM_ERROR_ENABLE == 1 */
/*****************************************************************************
 *  Work Around for APMU
 *****************************************************************************/
    wa_setting_apmu();

/*****************************************************************************
 *  Output boot message
 *****************************************************************************/
    print_boot_msg();
/*****************************************************************************
 *  WA for V4H ver2.0
 *****************************************************************************/
    uint32_t Prr_Cut;
    Prr_Cut = mem_read32(PRR) & PRR_CUT_MASK;

    if (Prr_Cut >= PRR_PRODUCT_20)
    {
        /* IPL setting for V4H v2.x leak current reduce */
        set_leak_current_reduce_for_v4h2x();

        wa_for_v4h2();
    }

    /* WA OTLINT-5556 increased latency: APMU FRSTCTRL bit[29] disable */
    tmp = mem_read32(BASE_APMU_ADDR + 0x68U);
    tmp = tmp & ~(1 << 29);
    mem_write32((BASE_APMU_ADDR + 0x68U), tmp);

#if (ECM_ENABLE == 1)
/*****************************************************************************
 *  ECC and EDC Initialize
 *****************************************************************************/
    edc_axi_enable();
    edc_vram_enable();
#endif

#if (SAN_ENABLE == 1)
    /* step(7) Initial Checks */
    SM_6_3_1();
    SM_6_3_2();
    SM_5_3_6();
    SM_5_1();
    SM_6_23();
    SM_4_21();
    SM_5_1_6();
    SM_5_2_6();
    SM_6_2();
    SM_6_11();
    SM_6_14_6();
    SM_6_22();
    SM_6_24_2();
    SM_6_24_3();
    SM_6_29();
    SM_6_30();
    PMIC_SM_27A();
    PMIC_SM_27B();
    PMIC_SM_27C();
    PMIC_SM_27D();

    /* step(8) start the timers for Periodical checks */

    check_SoC_Activation();
    /* step(9) send the first WDT message */
    PMIC_SM_12_wdt();
#endif
/*****************************************************************************
 *  Setting Access protection
 *****************************************************************************/
    /* Region ID access protection */
    rgid_protection();
    ram_protection();

    /* Change the Region ID (Master) of the Module used for data transfer to a temporary setting.  */
    set_master_rgid_4_tfr_mod();

/*****************************************************************************
 *  Clear ECM Status Bits
 *****************************************************************************/
    /* Check the V4H is v1.0 */
    if(Prr_Cut == PRR_PRODUCT_10)
    {
        clear_ecm_st_axi();
    }
    store_time_checkpoint("init_done", 0);

/*****************************************************************************
 *  Load Certficate from QSPI
 *****************************************************************************/
    int slot;

    slot = ab_select_slot();
#ifndef STRICT_AB_BOOTING
#warning "STRICT_AB_BOOTING is not defined, using default slot selection"
    if (slot < 0) {
        ERROR("Failed to select slot: %d, using default slot\n", slot);
        slot = 0;
    } else
#endif
    NOTICE("slot: %d\n", slot);

    /* Load content certificate */
    ca_load_num = load_content_cert(slot);
    store_time_checkpoint("load_cert_done",
        CONTENT_CERT_INFO_SIZE + ca_load_num*CONTENT_CERT_SIZE);

    /* Get load information */
    load_init(li, slot);

    /* verify the each content certs before Image load */
    preload_verify_cntcert(li);

/*****************************************************************************
 *  Authenticate Software minimum version table
 *****************************************************************************/
    auth_min_ver_tbl(li);
    store_time_checkpoint("verify_cert_done", 0);

/*****************************************************************************
 *  Load Cx IPL from Flash
 *****************************************************************************/
    /* Start loading Cx IPL image from Flash into SDRAM */
    load_image(&li[CA_PROGRAM_ID]);

    /* The CA image to boot is CA IPL. */
    boot_ca_id = CA_PROGRAM_ID;

    /* boot CPU is CR */
    boot_cpu = RCAR_PWR_TARGET_CR;

    /* finish loading Cx IPL */
    load_end();
    store_time_checkpoint("load_Cx_IPL_done", li[CA_PROGRAM_ID].image_size);
#if (BOOT_TIME_CHECK != 0)
    gpio_N1305(2);
#endif
	/* Set V4H_SDRDES_1V8(GP1_23) to high before optee start */
	gpio_V4H_SERDES_1V8_en(1);
/*****************************************************************************
 *  Load Secure Firmware from Flash
 *****************************************************************************/
    /* Start loading Secure FW image from Flash into SDRAM */
    load_image(&li[SECURE_FW_ID]);
    store_time_checkpoint("load_Secure_FW_start (DMA-background)", li[SECURE_FW_ID].image_size);

    /* Authenticate of CA Program#n */
    rom_secureboot(&li[boot_ca_id + auth_count]);
    store_time_checkpoint("verify_Cx_IPL_done", 0);

    /* SystemRAM has an undefined initial value, clear the address of
     * the SystemRAM that is going to store set finish flag of RGID. */
    mem_write8(RGID_SET_RGID_FIN_FLG_ADDR, 0xFFU);

    /* Before Boot CPU, Set the division ratio for CPU operating frequency */
    adj_cr_variant_freq();

    /* boot CA */
    arm_cpu_on(boot_cpu, li[boot_ca_id].boot_addr);
    store_time_checkpoint("start_Cx_IPL_done", 0);

    /* finish loading Secure Firmware */
    load_end();
    store_time_checkpoint("load_Secure_FW_done", 0);

    /* load_secure data(for ICUMXB) */
    load_securedata(SECURE_FW_ID, slot);
    store_time_checkpoint("load_Secure_Data_start (DMA-background)", SECUREDATA_SIZE);

    /* Authenticate of Secure Firmware */
    rom_secureboot(&li[SECURE_FW_ID]);
    store_time_checkpoint("verify_Secure_FW_done", 0);

    /* finish loading secure data */
    load_end();
    store_time_checkpoint("load_Secure_Data_done", 0);
#if (BOOT_TIME_CHECK != 0)
    gpio_N1305(2);
#endif
    /* Finally Protection setting */
    rgid_protection_final();

    /* set RGID setting finish flag.
     * because polling from CX 2nd IPL. */
    mem_write8(RGID_SET_RGID_FIN_FLG_ADDR, RGID_SET_RGID_FIN_FLG_VAL);

#if (ECM_ENABLE == 1)
    /* Notice the ecc enable */
    NOTICE("Enabled EDC for AXI-Bus, RT-VRAM. \n");
#endif
    NOTICE("Load finish.\n");
    // ip_release();

    store_time_checkpoint("done (Jumping to [SECURE_FW_ID].boot_addr)", 0);
    print_time_checkpoints();
    return get_cfremap_addr(li[SECURE_FW_ID].boot_addr);
}
/* End of function loader_main(void) */

void clear_ecm_st_axi(void)
{
    mem_write32(ICISTPSTPER000,0x30U);
    mem_write32(EDCSTRT20, 0x10U);
    mem_write32(TIDSTRT20, 0x12U);
}/* End of function clear_ecm_st_axi(void) */

static void wa_for_v4h2(void)
{
    uint32_t tmp;

    /* 1. Release of pseudo power shutdown */
    /* Process moved to function set_leak_current_reduce_for_v4h2x(). */

    /* 2. Reset ON for A2CN0 and Reset OFF for A2CN0 */
    tmp = mem_read32(BASE_CPG_ADDR + 0x2C00U);  /* Software Reset Register 0 (SRCR0) */
    tmp = tmp | (0x1U << 23U);
    mem_write32(CPG_CPGWPR, ~tmp);
    mem_write32((BASE_CPG_ADDR + 0x2C00U),tmp);

    tmp = mem_read32(BASE_CPG_ADDR + 0x2C80U);  /* Software Reset Clearing Register 0 (SRSTCLR0) */
    tmp = tmp | (0x1U << 23U);
    mem_write32(CPG_CPGWPR, ~tmp);
    mem_write32((BASE_CPG_ADDR + 0x2C80U),tmp);

    /* Unlock the ECM register protect. */
    mem_write32(ECMWPCNTR, 0xACCE0001);

    /* 3. ECM Enable */
    tmp = mem_read32(BASE_ECM_ADDR + 0x50018U); /* ECM Error Control Register 6 (ECMERRCTLR6) */
    tmp = tmp | (0xfU << 24U);
    mem_write32((BASE_ECM_ADDR + 0x50018U),tmp);

    /* Lock the ECM register protect. */
    mem_write32(ECMWPCNTR, 0xACCE0000U);
}/* End of function wa_for_v4h2(void) */

static void set_leak_current_reduce_for_v4h2x(void)
{
    uint32_t reg;
    const uint32_t pdrsr_tbl[PDR_MAX] = { /* SYSC PDRSR register table */
        SYSC_PDRSR43,   /* A3IR */
        SYSC_PDRSR42,   /* A2CN0 */
        SYSC_PDRSR41    /* A1CNN0 */
    };
    const uint32_t pdroncr_tbl[PDR_MAX] = { /* SYSC PDRONCR register table */
        SYSC_PDRONCR43, /* A3IR */
        SYSC_PDRONCR42, /* A2CN0 */
        SYSC_PDRONCR41  /* A1CNN0 */
    };

    /* Power ON / OFF process complete interrupt enable for PDR41-PDR43 */
    reg  = mem_read32(SYSC_SYSCIER1);
    reg |= (SYSC_BIT_PDR41 | SYSC_BIT_PDR42 | SYSC_BIT_PDR43);
    mem_write32(SYSC_SYSCIER1, reg);
    INFO("SYSCIER1(0x%08x) = %08x\n",SYSC_SYSCIER1,mem_read32(SYSC_SYSCIER1));

    /* MASK complete interrupt request to INTC for PDR41-PDR43 */
    reg  = mem_read32(SYSC_SYSCIMR1);
    reg |= (SYSC_BIT_PDR41 | SYSC_BIT_PDR42 | SYSC_BIT_PDR43);
    mem_write32(SYSC_SYSCIMR1, reg);
    INFO("SYSCIMR1(0x%08x) = %08x\n",SYSC_SYSCIMR1,mem_read32(SYSC_SYSCIMR1));

    for(uint32_t loop = 0U; loop < PDR_MAX; loop++)
    {
        /* Check PDRn Power on state */
        reg  = mem_read32(pdrsr_tbl[loop]);
        INFO("PDRSR(0x%08x) = %08x\n",pdrsr_tbl[loop],reg);
        reg &= SYSC_PDRSR_PWR_MASK;
        /* Only when the Power state of PDRn is OFF, the Power On Request is processed */
        if((reg == SYSC_PDRSR_PWROFF) == true)
        {
            /* Wait until SYSC is in Not busy status  */
            do{
                /* Get SYSC processing Power ON / OFF status */
                reg  = mem_read32(SYSC_SYSCSR);
                INFO("SYSCSR(0x%08x) = %08x\n", SYSC_SYSCSR, reg);
                reg &= SYSCSR_BUSY_MASK;
            } while((reg == SYSCSR_NOT_BUSY) != true);

            /* PDRn Power ON request */
            mem_write32(pdroncr_tbl[loop], SYSC_PDRONCR_PWRON);
            INFO("PDRONCRn(0x%08x) = %08x\n",pdroncr_tbl[loop],mem_read32(pdroncr_tbl[loop]));

            /* Power ON process complete interrupt status for PDR41-PDR43 */
            do{
                reg  = mem_read32(SYSC_SYSCISCR1);
                INFO("SYSCISCR1(0x%08x) = %08x\n", SYSC_SYSCISCR1, reg);
                reg &= (SYSC_BIT_PDR43 >> loop);    /* loop = 0:PDR43 1:PDR42 2:PDR41 */
            } while((reg !=SYSC_PDR_PWR_PROC) != true);

            /* Clear Power ON process complete interrupt status for PDR41-PDR43 */
            mem_write32(SYSC_SYSCISCR1, reg);
            INFO("SYSCISCR1(0x%08x) = %08x\n",SYSC_SYSCISCR1,mem_read32(SYSC_SYSCISCR1));
        }
    }

    /* Power ON / OFF process complete interrupt disable for PDR41-PDR43 */
    reg  = mem_read32(SYSC_SYSCIER1);
    reg &= ~(SYSC_BIT_PDR41 | SYSC_BIT_PDR42 | SYSC_BIT_PDR43);
    mem_write32(SYSC_SYSCIER1, reg);
    INFO("SYSCIER1(0x%08x) = %08x\n",SYSC_SYSCIER1,mem_read32(SYSC_SYSCIER1));

    /* MASK complete interrupt request to INTC for PDR41-PDR43 */
    reg  = mem_read32(SYSC_SYSCIMR1);
    reg &= ~(SYSC_BIT_PDR41 | SYSC_BIT_PDR42 | SYSC_BIT_PDR43);
    mem_write32(SYSC_SYSCIMR1, reg);
    INFO("SYSCIMR1(0x%08x) = %08x\n",SYSC_SYSCIMR1,mem_read32(SYSC_SYSCIMR1));
}
