/*
 * Copyright (c) 2018-2023 Renesas Electronics Corporation. All rights reserved.
 */

#include <stdio.h>
#include "common.h"
#include "main.h"
#include "devdrv.h"
#include "reg_rcargen3.h"
#include "d_armasm.h"
#include "timer.h"

#define DUMMY_SECURE_MONITOR	1U
#define DUMMY_U_BOOT		2U
#define DUMMY_TEE		3U

#define NORMAL_END	(0U)
#define ERROR_END	(1U)

#define	CL16MPHY	(16660000U)

/* Input Dummy Program waiting time (us) */
#define WAIT_TIME_US    (3000000U)

uint64_t gErrDdrAdd;
uint32_t gErrDdrData;
uint32_t gTrueDdrData;

static void StartMess(void);
static uint32_t CkExtendDdrRamCheck(void* ramAddr, uint64_t ramSize);
void bl2_init_generic_timer(void);
void rcar_set_log_time(void);

extern const uint8_t __RO_START__[1];
extern const uint8_t __STACKS_END__[1];

void Main(void)
{
	volatile uint32_t i;
	uint32_t ret;
	char str[64];
	uint32_t product;

#if (DUMMY == DUMMY_SECURE_MONITOR)
    /* Generic Timer initialization is performed at only once. */
    generic_timer_init();
#endif /* (DUMMY == DUMMY_SECURE_MONITOR) */

    micro_wait(WAIT_TIME_US);

	product = *((volatile uint32_t*)PRR) & PRR_PRODUCT_MASK;
	
#if DUMMY == DUMMY_U_BOOT
 #if SDRAM_CHECK == 1
  #if DCACHE == 1
	DCacheEnable();
  #endif
	/* 0x04_20000000 -- 0x04_FFFFFFFF */
	rcar_set_log_time();
    /* V4H DDR0 area: H'04_0000 0000 ~ H'04_7FFF FFFF (2 Gbytes) */
    /* V4H DDR1 area: H'04_8000 0000 ~ H'04_FFFF FFFF (2 Gbytes) */
    /* V4M DDR0 area: H'04_0000 0000 ~ H'04_FFFF FFFF (4 Gbytes) */
    PutStr("SDRAM verify test 0x04_20000000 -- 0x04_CCCCFFFF",1);
	ret = CkExtendDdrRamCheck((void*)0x0420000000U, 0xACCD0000U);
	if (ret != NORMAL_END)
	{
		rcar_set_log_time();
		PutStr("SDRAM verify NG",1);
		rcar_set_log_time();
		PutStr("Error Address:0x",0);
		Data2HexAscii_64(gErrDdrAdd,str,8);
		PutStr(str,1);
		rcar_set_log_time();
		PutStr("Error Data     :0x",0);
		Data2HexAscii(gErrDdrData,str,4);
		PutStr(str,1);
		rcar_set_log_time();
		PutStr("Expected Data  :0x",0);
		Data2HexAscii(gTrueDdrData,str,4);
		PutStr(str,1);
	}
	else
	{
		rcar_set_log_time();
		PutStr("SDRAM verify OK",1);
	}

	rcar_set_log_time();

    if (product == PRR_PRODUCT_V4H)
    {
        /* V4H DDR2 area: H'06_0000 0000 ~ H'06_7FFF FFFF (2 Gbytes) */
        /* V4H DDR3 area: H'06_8000 0000 ~ H'06_FFFF FFFF (2 Gbytes) */
        PutStr("SDRAM verify test 0x06_00000000 -- 0x06_CCCCFFFF",1);
        ret = CkExtendDdrRamCheck((void*)0x0600000000U, 0x00CCCD0000U);
    } else { /* (product == PRR_PRODUCT_V4M) */
        /* V4M DDR1 area: H'05_0000 0000 ~ H'05_FFFF FFFF (4 Gbytes) */
        PutStr("SDRAM verify test 0x05_00000000 -- 0x05_CCCCFFFF",1);
        ret = CkExtendDdrRamCheck((void*)0x0500000000U, 0x00CCCD0000U);
    }
	if (ret != NORMAL_END)
	{
		rcar_set_log_time();
		PutStr("SDRAM verify NG",1);
		rcar_set_log_time();
		PutStr("Error Address:0x",0);
		Data2HexAscii_64(gErrDdrAdd,str,8);
		PutStr(str,1);
		rcar_set_log_time();
		PutStr("Error Data     :0x",0);
		Data2HexAscii(gErrDdrData,str,4);
		PutStr(str,1);
		rcar_set_log_time();
		PutStr("Expected Data  :0x",0);
		Data2HexAscii(gTrueDdrData,str,4);
		PutStr(str,1);
	}
	else
	{
		rcar_set_log_time();
		PutStr("SDRAM verify OK",1);
	}

 #endif /* SDRAM_CHECK == 1 */
#endif /* DUMMY == DUMMY_U_BOOT */
	
	StartMess();

#if DUMMY == DUMMY_U_BOOT
	while(1) {
		__asm__ volatile ("wfi");
	}
#elif DUMMY == DUMMY_TEE

#elif DUMMY == DUMMY_SECURE_MONITOR
    
#endif

}


static void StartMess(void)
{
#if DUMMY == DUMMY_SECURE_MONITOR
	PutStr("  ",1);
	rcar_set_log_time();
	PutStr("Dummy Secure Monitor",1);
	rcar_set_log_time();
	PutStr("Dummy Secure Monitor boot end",1);
#elif DUMMY == DUMMY_TEE
	PutStr("  ",1);
	rcar_set_log_time();
	PutStr("Dummy TEE",1);
	rcar_set_log_time();
	PutStr("Dummy TEE boot end",1);
#else
	PutStr("  ",1);
	rcar_set_log_time();
	PutStr("Dummy U-Boot",1);
	rcar_set_log_time();
	PutStr("Dummy U-Boot boot end",1);
#endif
}

uint32_t CkExtendDdrRamCheck(void* ramAddr, uint64_t ramSize)
{
//#ifdef COM_DDRCK_ON

    volatile uint64_t *read_adr;
    uint64_t data;
    uint32_t loop, i;
    char str[64];

    read_adr = (uintptr_t *)ramAddr;

    /* Write */
    data = 0x5A5A5A5A5A5A5A5A;
    for (loop = 0; loop < ramSize/8; loop++) {
    	read_adr[loop] = data;
    }

    /* Verify */
    data = 0x5A5A5A5A5A5A5A5A;
    for (loop = 0; loop < ramSize/8; loop++) {
        if (read_adr[loop] != data) {
            gErrDdrAdd   = (uintptr_t)&read_adr[loop];
            gErrDdrData  = read_adr[loop];
            gTrueDdrData = data;
            return(ERROR_END);
        }
    }

    /* Write */
    data = 0xA5A5A5A5A5A5A5A5;
    for (loop = 0; loop < ramSize/8; loop++) {
        read_adr[loop] = data;
    }

    /* Verify */
    data = 0xA5A5A5A5A5A5A5A5;
    for (loop = 0; loop < ramSize/8; loop++) {
        if (read_adr[loop] != data) {
            gErrDdrAdd   = (uintptr_t)&read_adr[loop];
            gErrDdrData  = read_adr[loop];
            gTrueDdrData = data;
            return(ERROR_END);
        }
    }

    /* Write */
    data = 0x0123456789ABCDEF;
    for (loop = 0; loop < ramSize/8; loop++) {
        read_adr[loop] = data;
        data += 0x1111111111111111;
    }

    /* Verify */
    data = 0x0123456789ABCDEF;
    for (loop = 0; loop < ramSize/8; loop++) {
        if (read_adr[loop] != data) {
            gErrDdrAdd   = (uintptr_t)&read_adr[loop];
            gErrDdrData  = read_adr[loop];
            gTrueDdrData = data;
            return(ERROR_END);
        }
        data += 0x1111111111111111;
    }

    return(NORMAL_END);
}

void rcar_set_log_time(void)
{
	uint64_t now_time;
	uint64_t freq;
	uint64_t second;
	uint64_t micro_sec;
	uint64_t t_log[2][15];
	int32_t i;
	int32_t start_counter;

	__asm__ volatile ("mrs %0, cntpct_el0" : "=r" (now_time));
	__asm__ volatile ("mrs %0, cntfrq_el0" : "=r" (freq));
	if (freq == 0U) { /* for zero division	*/
		second = 0U;
		micro_sec = 0U;
	} else {
		second = now_time / freq;
		micro_sec = ((now_time % freq) * 1000000U) / freq;
	}
	i = 14; /* counter initialize	*/
	do {
		t_log[0][i] = second % 10U;
		second = second / 10U;
		i--;
	} while (second != 0U);
	for (; i >= 10; i--) {
		t_log[0][i] = (int)' ';
	}
	start_counter = i + 1;
	t_log[1][0] = micro_sec / 100000U;
	micro_sec %= 100000U;
	t_log[1][1] = micro_sec / 10000U;
	micro_sec %= 10000U;
	t_log[1][2] = micro_sec / 1000U;
	micro_sec %= 1000U;
	t_log[1][3] = micro_sec / 100U;
	micro_sec %= 100U;
	t_log[1][4] = micro_sec / 10U;
	t_log[1][5] = micro_sec % 10U;

	(void)PutChar((int)'[');
	for (i = start_counter; i < 15; i++) {
		if (t_log[0][i] <= 9) {
			(void)PutChar((int)((int)t_log[0][i] + (int)0x30));
		} else {
			(void)PutChar((int)' ');
		}
	}
	(void)PutChar((int)'.');
	for (i = 0; i < 6; i++) {
		(void)PutChar((int)((int)t_log[1][i] + (int)0x30));
	}
	(void)PutChar((int)']');
	(void)PutChar((int)' ');
}
