/**
    NVT evb board file
    To handle na51055 basic init.
    @file       na51055evb.c
    @ingroup
    @note
    Copyright   Novatek Microelectronics Corp. 2019.  All rights reserved.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 as
    published by the Free Software Foundation.
*/


#include <common.h>
#include <asm/mach-types.h>
#include <asm/nvt-common/nvt_common.h>
#include <asm/nvt-common/rcw_macro.h>
#include <asm/arch/IOAddress.h>
#include <asm/arch/na51055evb.h>
#include <linux/libfdt.h>

#ifdef CONFIG_SECURE_BOOT_WRITE_KEY_EN
#include <asm/gpio.h>

#include <asm/arch/efuse_protected.h>
#include "ab_secboot_key_info.h"
#endif

DECLARE_GLOBAL_DATA_PTR;
extern int nvt_mmc_init(int id);
extern int na51055_eth_initialize(bd_t *bis);
const char *boardinfo[] = {
	"Board:"_CHIP_NAME_"EVB\n"
};

#ifdef AB_MODIFIED
#ifdef CONFIG_FW_MULTI_PARTITION_ENABLE
extern bool b_load_ab_boot;
#endif

#ifdef CONFIG_AB_UPGRADE_LED_SINGLE
static void gpio_set_output(u32 pin)
{
	u32 reg_data;
	u32 ofs = (pin >> 5) << 2;

	pin &= (32 - 1);

	reg_data = INW(IOADDR_GPIO_REG_BASE + 0x20 + ofs);
	reg_data |= (1 << pin);    //output
	OUTW(IOADDR_GPIO_REG_BASE + 0x20 + ofs, reg_data);
}

static void gpio_set_pin(u32 pin, bool status)
{
	u32 tmp;
	u32 ofs = (pin >> 5) << 2;

	pin &= (32 - 1);
	tmp = (1 << pin);
	
	if(status)
		OUTW(IOADDR_GPIO_REG_BASE + 0x40 + ofs, tmp); // 1
	else
		OUTW(IOADDR_GPIO_REG_BASE + 0x60 + ofs, tmp); // 0
}

static void ab_get_fwup_led_info(uint32_t* led_pin,bool* active)
{
	uint32_t *cell = NULL;
	ulong fdt_addr = nvt_readl((ulong)nvt_shminfo_boot_fdt_addr);
	int nodeoffset = fdt_path_offset((const void*)fdt_addr, "/ab_uboot_fwup_led");
	if (nodeoffset >= 0) {

		cell = (uint32_t*)fdt_getprop((const void*)fdt_addr, nodeoffset, "fwup_status_led_pin", NULL);
		if (cell > 0) {
			*led_pin= be32_to_cpu(cell[0]);
			printf("Get fwup_status_led_pin = %d\n",*led_pin);
		}
		else
		{
			ab_err_print("Get fwup_status_led_pin fail\n");
		}
		
		cell = (uint32_t*)fdt_getprop((const void*)fdt_addr, nodeoffset, "led_active_status", NULL);
		if (cell > 0) {
			*active= be32_to_cpu(cell[0]);
			printf("Get led_active_status = %d\n",*active);
		}
		else
		{
			ab_err_print("Get led_active_status fail\n");
		}
		
	}
	else
	{
		ab_err_print("get nodeoffset of ab_uboot_fwup_led fail.\n");
	}
}

static void ab_fwup_led_ctrl(uint32_t led_pin,bool active,int fwup_ret)
{
	uint8_t ui_cnt=0;
	
	switch(fwup_ret)
	{
		case ERR_NVT_UPDATE_FAILED:
			gpio_set_output(led_pin);
			while(1)
			{
				ui_cnt = 1;
				while(ui_cnt--)
				{
					gpio_set_pin(led_pin,active);
					mdelay(200);
					gpio_set_pin(led_pin,!active);
					mdelay(200);
				}
				mdelay(1000);
			}
			break;
		case ERR_NVT_UPDATE_OPENFAILED:// no sd card case
		case ERR_NVT_UPDATE_READ_FAILED:
			gpio_set_output(led_pin);
			while(1)
			{
				ui_cnt = 2;
				while(ui_cnt--)
				{
					gpio_set_pin(led_pin,active);
					mdelay(200);
					gpio_set_pin(led_pin,!active);
					mdelay(200);
				}
				mdelay(1000);
			}
			break;
		case ERR_NVT_EFUSE_WRITE_FAILED:
			gpio_set_output(led_pin);
			while(1)
			{
				ui_cnt = 4;
				while(ui_cnt--)
				{
					gpio_set_pin(led_pin,active);
					mdelay(150);
					gpio_set_pin(led_pin,!active);
					mdelay(150);
				}
				mdelay(1500);
			}
			break;
		case ERR_NVT_EFUSE_WRITE_SUCCESS:
			gpio_set_output(led_pin);
			while(1)
			{
				ui_cnt = 1;
				while(ui_cnt--)
				{
					gpio_set_pin(led_pin,active);
					mdelay(1000);
					//gpio_set_pin(led_pin,!active);
				}
				mdelay(1000);
			}
			break;
		case ERR_NVT_UPDATE_NO_NEED:
			/*Do nothing*/
			break;
		default:
			break;
	}
}

#define AB_FWUP_LED_TOGGLE_TIME 200000//us
bool b_is_ab_fwup=0;
uint32_t ui_ab_fwup_led_pin=0;
bool b_ab_fwup_led_active_status=0;
unsigned long ul_fwup_led_last_toggle_time=0;
bool b_ab_fwup_led_state=0;

void ab_fwup_led_toggle(void)
{
	if( b_is_ab_fwup && (get_nvt_timer0_cnt() - ul_fwup_led_last_toggle_time >= AB_FWUP_LED_TOGGLE_TIME) )
	{
		ul_fwup_led_last_toggle_time = get_nvt_timer0_cnt();
		b_ab_fwup_led_state ^= 1;
		gpio_set_pin( ui_ab_fwup_led_pin, b_ab_fwup_led_state);
	}
}

void ab_fwup_led_toggle_init(void)
{
	b_is_ab_fwup = 1;

	ab_get_fwup_led_info(&ui_ab_fwup_led_pin, &b_ab_fwup_led_active_status);
	gpio_set_output(ui_ab_fwup_led_pin);
	gpio_set_pin(ui_ab_fwup_led_pin, b_ab_fwup_led_active_status);
	b_ab_fwup_led_state = b_ab_fwup_led_active_status;
}

void ab_fwup_led_toggle_deinit(void)
{
	if(b_is_ab_fwup == 1)
	{
		b_is_ab_fwup = 0;

		gpio_set_pin(ui_ab_fwup_led_pin, !b_ab_fwup_led_active_status);
		b_ab_fwup_led_state = !b_ab_fwup_led_active_status;
	}
}

bool is_ab_fwup_led_toggle_processing(void)
{
	return b_is_ab_fwup;
}
#endif

static void ab_get_efuse_en_pin_info(uint32_t* led_pin,bool* active)
{
	uint32_t *cell = NULL;
	ulong fdt_addr = nvt_readl((ulong)nvt_shminfo_boot_fdt_addr);
	int nodeoffset = fdt_path_offset((const void*)fdt_addr, "/ab_uboot_efuse_en_pin");
	if (nodeoffset >= 0) {

		cell = (uint32_t*)fdt_getprop((const void*)fdt_addr, nodeoffset, "efuse_status_pin", NULL);
		if (cell > 0) {
			*led_pin= be32_to_cpu(cell[0]);
			printf("Get efuse_status_pin = %d\n",*led_pin);
		}
		else
		{
			ab_err_print("Get efuse_status_pin fail\n");
		}
		
		cell = (uint32_t*)fdt_getprop((const void*)fdt_addr, nodeoffset, "efuse_pin_active_status", NULL);
		if (cell > 0) {
			*active= be32_to_cpu(cell[0]);
			printf("Get efuse_pin_active_status = %d\n",*active);
		}
		else
		{
			ab_err_print("Get efuse_pin_active_status fail\n");
		}
		
	}
	else
	{
		ab_err_print("get nodeoffset of ab_uboot_fwup_led fail.\n");
	}
}

static int ab_check_test_mode_pin(void)
{
    int value = 0;
    u32 gpio_ofs = 0;
    u32 tmp = 0;
    u32 status = 0;
    
    uint32_t test_mode_pin    = 0;
    bool     test_mode_active = TRUE;
        
    ab_get_efuse_en_pin_info(&test_mode_pin,&test_mode_active);
    ab_dbg_print("test_pin is %d, active is %d\r\n", test_mode_pin,test_mode_active);
    gpio_ofs = (test_mode_pin >> 5) << 2;
    test_mode_pin &= (32 - 1);
	tmp = (1 << test_mode_pin);
    
    value = INW(IOADDR_GPIO_REG_BASE+(gpio_ofs));
    status = (value&tmp)>>test_mode_pin;
    ab_dbg_print("Pin value=0x%X,pin_status=%d\r\n", value,status);
    if(status == test_mode_active)
    {
        ab_dbg_print("[%s]Equal,status=%d,active=%d\r\n", __func__,status,test_mode_active);
        return 1;
    }
    else
    {
        ab_dbg_print("[%s]Diff,status=%d,active=%d\r\n", __func__,status,test_mode_active);
        return 0;
    }
}
#endif

void reset_cpu(unsigned long ignored)
{
	nvt_ivot_reset_cpu();
}

/**
 * @brief board_early_init_f
 *
 * @return 0
 */
int board_early_init_f(void)
{
	//nvt_ipc_init();
	return 0;
}

/**
 * @brief board_init
 *
 * @return 0
 */
int board_init(void)
{
	int ret = 0;

#ifdef CONFIG_NVT_IVOT_BOOT_FAST
	char command[100] = {0};
	sprintf(command, "nvt_uart_disable");
	ret = run_command(command, 0);
#endif

	nvt_tm0_cnt_beg = get_nvt_timer0_cnt();

	printf("Relocation to 0x%08lx, Offset is 0x%08lx sp at %08lx\n", gd->relocaddr, gd->reloc_off, gd->start_addr_sp);
	nvt_shminfo_init();
	timer_init();
	ret = nvt_fdt_init(false);
	if (ret < 0) {
		printf("fdt init fail\n");
	}

	#ifdef CONFIG_NVT_FPGA_EMULATION
	nvt_writel(0xFFFFFFFF, (ulong)0xF0E40048);
	printf("0xF0E40048 = 0x%08x\r\n", nvt_readl(0xF0E40048));
	printf("0xF0E4004C = 0x%08x\r\n", nvt_readl(0xF0E4004C));
	#endif

	ret = nvt_board_init_early();
	if (ret < 0) {
		printf("board init early fail\n");
		return 0;
	}

	nvt_print_system_info();

	return 0;
}

int board_mmc_init(bd_t *bis)
{
	int ret = 0, i;
	int CONFIG_NVT_MMC_CHANNEL = 0;

#ifdef CONFIG_NVT_MMC_CHANNEL_SDIO1
	CONFIG_NVT_MMC_CHANNEL |= 1 << 0;
#endif

#ifdef CONFIG_NVT_MMC_CHANNEL_SDIO2
	CONFIG_NVT_MMC_CHANNEL |= 1 << 1;
#endif

#ifdef CONFIG_NVT_MMC_CHANNEL_SDIO3
	CONFIG_NVT_MMC_CHANNEL |= 1 << 2;
#endif

	for (i = 0; i < CONFIG_NVT_MMC_MAX_NUM; i++) {
		if((CONFIG_NVT_MMC_CHANNEL >> i) & 0x1) {
			ret = nvt_mmc_init(i);
			if(ret)
				break;
		}
	}

	return ret;
}

int board_eth_init(bd_t *bis)
{
	int rc = 0;

#ifdef CONFIG_ETHNET
	rc = na51055_eth_initialize(bis);
#endif

	return rc;
}

static int ab_secboot_write_key_enable(void)
{
    
#ifdef CONFIG_SECURE_BOOT_WRITE_KEY_EN
    int testidx=0;
    u32     key_set = EFUSE_OTP_1ST_KEY_SET_FIELD;
    UINT8   pKey[16];
    UINT8   read_key_data[16];
    s32     result;

    unsigned int field_index = 0;
	unsigned int key_value = 0;
	unsigned int key_field_index = 0;
    
	ab_dbg_print("AES key: [ ");
	for(testidx=0;testidx<16;testidx++)
		ab_dbg_print("%X,",ab_secboot_aeskey[testidx]);
	ab_dbg_print("]\n");
	
	ab_dbg_print("RSA Hash1: [ ");
	for(testidx=0;testidx<16;testidx++)
		ab_dbg_print("%X,",ab_secboot_rsahash1[testidx]);
	ab_dbg_print("]\n");
	
	ab_dbg_print("RSA Hash2: [ ");
	for(testidx=0;testidx<16;testidx++)
		ab_dbg_print("%X,",ab_secboot_rsahash2[testidx]);
	ab_dbg_print("]\n");

    ab_dbg_print("is_secure_enable()=%d\r\n", is_secure_enable());
    
    //write key
    #if 1
    //aes
    key_set = EFUSE_OTP_1ST_KEY_SET_FIELD;
	memcpy((void *)pKey, (void *)ab_secboot_aeskey, 16);
    result = otp_write_key(key_set, pKey);
    if (result < 0) {
		ab_dbg_print("Write [%d] key operation fail [%d] \r\n", (int)(key_set), result);
		goto write_key_err;
	} else {
		ab_dbg_print("Write [%d] key operation success\r\n", (int)(key_set));
	}
    //RSA hash1
    key_set = EFUSE_OTP_2ND_KEY_SET_FIELD;
	memcpy((void *)pKey, (void *)ab_secboot_rsahash1, 16);
    result = otp_write_key(key_set, pKey);
    if (result < 0) {
		ab_dbg_print("Write [%d] key operation fail [%d] \r\n", (int)(key_set), result);
		goto write_key_err;
	} else {
		ab_dbg_print("Write [%d] key operation success\r\n", (int)(key_set));
	}
    //RSA hash2
    key_set = EFUSE_OTP_3RD_KEY_SET_FIELD;
	memcpy((void *)pKey, (void *)ab_secboot_rsahash2, 16);
    result = otp_write_key(key_set, pKey);
    if (result < 0) {
		ab_dbg_print("Write [%d] key operation fail [%d] \r\n", (int)(key_set), result);
		goto write_key_err;
	} else {
		ab_dbg_print("Write [%d] key operation success\r\n", (int)(key_set));
	}
    #endif
    //Read key
    int i=0,key_idx=0;
    key_set = EFUSE_OTP_1ST_KEY_SET_FIELD;
    field_index = 16;

    for(key_idx =0;key_idx<3;key_idx++)
    {
        switch(key_idx)
        {
            case 0:
                key_set = EFUSE_OTP_1ST_KEY_SET_FIELD;
                field_index = 16;
                break;
            case 1:
                key_set = EFUSE_OTP_2ND_KEY_SET_FIELD;
                field_index = 20;
                break;
            case 2:
                key_set = EFUSE_OTP_3RD_KEY_SET_FIELD;
                field_index = 24;
                break;
        }
        for (i = 0; i < 4; i++) {
    		key_field_index = field_index + i;
    		key_value = otp_key_manager(key_field_index);
    		ab_dbg_print("\033[0;33mkey[%2d][0x%08x]\033[0m\n", key_field_index, key_value);
    		read_key_data[i * 4] = (unsigned char)(key_value & 0x000000ff);
    		read_key_data[i * 4 + 1] = (unsigned char)((key_value & 0x0000ff00) >> 8);
    		read_key_data[i * 4 + 2] = (unsigned char)((key_value & 0x00ff0000) >> 16);
    		read_key_data[i * 4 + 3] = (unsigned char)((key_value & 0xff000000) >> 24);
    	}
        ab_dbg_print("key value:%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\r\n",
    		   read_key_data[0], read_key_data[1], read_key_data[2], read_key_data[3],
    		   read_key_data[4], read_key_data[5], read_key_data[6], read_key_data[7],
    		   read_key_data[8], read_key_data[9], read_key_data[10], read_key_data[11],
    		   read_key_data[12], read_key_data[13], read_key_data[14], read_key_data[15]);
    }

    //Add enable secure boot config in efuse
    #if 1
    ab_dbg_print("=>[secure_en]\r\n");
    efuse_secure_en();
    ab_dbg_print("is_secure_enable()=%d\r\n", is_secure_enable());

    ab_dbg_print("=>[efuse_data_area_encrypt_en]\r\n");
    efuse_data_area_encrypt_en();
    ab_dbg_print("is_data_area_encrypted()=%d\r\n", is_data_area_encrypted());

    ab_dbg_print("=>[efuse_signature_rsa_en]\r\n");
    efuse_signature_rsa_en();
    ab_dbg_print("is_signature_rsa()=%d\r\n", is_signature_rsa());

    ab_dbg_print("=>[efuse_signature_rsa_chksum_en]\r\n");
    efuse_signature_rsa_chksum_en();
    ab_dbg_print("is_signature_rsa_chsum_enable()=%d\r\n", is_signature_rsa_chsum_enable());
    #endif
    //end
    ab_dbg_print("             is_secure_enable()=%d\r\n", is_secure_enable());
	ab_dbg_print("       is_data_area_encrypted()=%d\r\n", is_data_area_encrypted());
	ab_dbg_print("             is_signature_rsa()=%d\r\n", is_signature_rsa());
	ab_dbg_print("is_signature_rsa_chsum_enable()=%d\r\n", is_signature_rsa_chsum_enable());
	ab_dbg_print("           is_JTAG_DISABLE_en()=%d\r\n", is_JTAG_DISABLE_en());
	ab_dbg_print("        is_1st_key_programmed()=%d\r\n", is_1st_key_programmed());
	ab_dbg_print("        is_2nd_key_programmed()=%d\r\n", is_2nd_key_programmed());
	ab_dbg_print("        is_3rd_key_programmed()=%d\r\n", is_3rd_key_programmed());
	ab_dbg_print("        is_4th_key_programmed()=%d\r\n", is_4th_key_programmed());
	ab_dbg_print("        is_5th_key_programmed()=%d\r\n", is_5th_key_programmed());

    
    return 0;
    write_key_err:
        ab_dbg_print("[%s]write key fail.\n",__func__);
        return 1;
#endif
}

#ifdef CONFIG_NVT_IVOT_SOC_FW_UPDATE_SUPPORT
static int nvt_handle_fw_abin(void)
{
	int ret = 0,secboot_ret;
	char cmdline[1024] = {0};
	char buf[1024] = {0};
    
    #ifdef CONFIG_SECURE_BOOT_WRITE_KEY_EN
    uint8_t is_test_mode = 0;
    is_test_mode = ab_check_test_mode_pin();
    
    if(is_test_mode)
    {
		#ifdef CONFIG_AB_UPGRADE_LED_SINGLE
        uint32_t led1_pin=0;
        bool led1_active = TRUE;
        ab_get_fwup_led_info(&led1_pin,&led1_active);
        #endif
		
        secboot_ret = ab_secboot_write_key_enable();
        if(secboot_ret)
        {
            ab_err_print("%s(%d) secboot write key fail.\n",__func__, __LINE__);
            //Add error handle
            while(1)
            {
                //efuse write fail led case.
                ab_err_print("%s(%d) efuse write fail led case.\n",__func__, __LINE__);
				ab_fwup_led_ctrl(led1_pin,led1_active,ERR_NVT_EFUSE_WRITE_FAILED);
            }
        }
        else
        {
            //Add success handle
            ab_err_print("%s(%d) secboot write key success.\n",__func__, __LINE__);
            while(1)
            {
                ab_err_print("%s(%d) efuse write success led case.\n",__func__, __LINE__);
				ab_fwup_led_ctrl(led1_pin,led1_active,ERR_NVT_EFUSE_WRITE_SUCCESS);
            }
        }
    }
	else
	{
		ab_dbg_print("%s(%d) efuse write test pin disable.\n",__func__, __LINE__);
	}
    #else
    printf("[%s]Secure efuse write key config is no define\n",__func__);
    #endif
    
	/* To handle firmware update */
	ret = nvt_fw_update(false);

    #ifdef AB_MODIFIED
    #ifdef CONFIG_FW_MULTI_PARTITION_ENABLE
    // a/b partition modify
    printf("[%s] Ability nvt_fw_update ret: %d\n",__func__,ret);
    u32 nand_addr = 0,nand_size = 0, nand_block_size = 0;
    int nodeoffset, len;
    u32 *pui_cell = NULL;
    char * pc_string_blk_size = NULL;
    /* get fdt info*/
    ulong fdt_addr = nvt_readl((ulong)nvt_shminfo_boot_fdt_addr);
    nodeoffset = fdt_path_offset((const void*)fdt_addr, "/nand/partition_ab_boot");
    if (nodeoffset < 0) {
        ab_err_print("%s(%d) nodeoffset < 0\n",__func__, __LINE__);
        return false;
    }

    pui_cell = (u32 *)fdt_getprop((const void*)fdt_addr, nodeoffset, "reg", &len);
    if (len == 0) {
        ab_err_print("%s(%d) len = 0\n",__func__, __LINE__);
        return false;
    }

    nand_addr = __be32_to_cpu(pui_cell[1]);
    nand_size = __be32_to_cpu(pui_cell[3]);
    printf("[%s] nand_addr: 0x%x, nand_size: 0x%x\n",__func__,nand_addr,nand_size);

    nodeoffset = fdt_path_offset((const void*)fdt_addr, "/nvt_info");
    if (nodeoffset < 0) {
        ab_err_print("%s(%d) nodeoffset < 0\n",__func__, __LINE__);
        return false;
    }

    pc_string_blk_size = (u32 *)fdt_getprop((const void*)fdt_addr, nodeoffset, "EMBMEM_BLK_SIZE", &len);
    if (len == 0) {
        ab_err_print("%s(%d) len = 0\n",__func__, __LINE__);
        return false;
    }
    printf("[%s] len: %d, pc_string_blk_size: %s\n",__func__,len,pc_string_blk_size);
    nand_block_size = simple_strtoul(pc_string_blk_size, NULL, 16);

    int boot_ret = 0;
    if(ret == 0){
        boot_ret = ab_reset_bootup_config(nand_addr,nand_size,nand_block_size);
        if(boot_ret<0)
            ab_err_print("reset boot config fail.\n");
    }else{
        if(ab_load_bootup_config(nand_addr,nand_size,nand_block_size) == 0)
            b_load_ab_boot = true;
        else
        {
            boot_ret = ab_reset_bootup_config(nand_addr,nand_size,nand_block_size);
            if(boot_ret<0)
                ab_err_print("reset boot config fail.\n");
        }
    }
    #endif
    #endif
    
	if (ret < 0) {
        #ifdef AB_MODIFIED
        #ifdef CONFIG_AB_UPGRADE_LED_SINGLE
        uint32_t led1_pin=0;
        bool led1_active = TRUE;
        ab_get_fwup_led_info(&led1_pin,&led1_active);
        #endif
        #if CONFIG_AB_UPGRADE_LED
        extern int pwm_config(int, int, int);
        extern int pwm_enable(int);
        pwm_config(PWM_LED_GREEN_PGPIO, get_duty_ns(PWM_LED_FAIL_MODE_GREEN), get_period_ns(PWM_LED_FAIL_MODE_GREEN));
        pwm_config(PWM_LED_RED_PGPIO, get_duty_ns(PWM_LED_FAIL_MODE_RED), get_period_ns(PWM_LED_FAIL_MODE_RED));
        pwm_enable(PWM_LED_GREEN_PGPIO);
        pwm_enable(PWM_LED_RED_PGPIO);
        #endif
        #endif
		switch (ret) {
		case ERR_NVT_UPDATE_FAILED:
			#ifdef AB_MODIFIED
			ab_err_print("%sUpdate fail %s\r\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
			#ifdef CONFIG_AB_UPGRADE_LED_SINGLE
			ab_fwup_led_ctrl(led1_pin,led1_active,ERR_NVT_UPDATE_FAILED);
			#endif
			#else
			printf("%sUpdate fail %s\r\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
			#endif
			return -1;
			break;
		case ERR_NVT_UPDATE_OPENFAILED:
			#ifdef AB_MODIFIED
			ab_err_print("Open SD fail:%s No SD device? %s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);
			#ifdef CONFIG_AB_UPGRADE_LED_SINGLE
			if( is_ab_fwup_led_toggle_processing() )
			{//Open SD fail will also occur in normal boot.
				ab_fwup_led_ctrl(led1_pin,led1_active,ERR_NVT_UPDATE_OPENFAILED);
			}
			#endif
			#else
			printf("Open SD fail:%s No SD device? %s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);
			#endif
			break;
		case ERR_NVT_UPDATE_READ_FAILED:
			#ifdef AB_MODIFIED
			ab_err_print("%sRead SD fail %s\r\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
			#ifdef CONFIG_AB_UPGRADE_LED_SINGLE
			ab_fwup_led_ctrl(led1_pin,led1_active,ERR_NVT_UPDATE_READ_FAILED);
			#endif
			#else
			printf("%sRead SD fail %s\r\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
			#endif
			return -1;
			break;
		case ERR_NVT_UPDATE_NO_NEED:
			#ifdef AB_MODIFIED
			ab_err_print("%sNo need to update (%s) %s\r\n", ANSI_COLOR_YELLOW, get_nvt_bin_name(NVT_BIN_NAME_TYPE_FW), ANSI_COLOR_RESET);
			#else
			printf("%sNo need to update (%s) %s\r\n", ANSI_COLOR_YELLOW, get_nvt_bin_name(NVT_BIN_NAME_TYPE_FW), ANSI_COLOR_RESET);
			#endif
			break;
		default:
			break;
		}
		ret = nvt_fdt_init(true);
		if (ret < 0) {
			#ifdef AB_MODIFIED
			ab_err_print("modelext init fail\n");
			#else
			printf("modelext init fail\n");
			#endif
		}
	} else{
	    #ifdef AB_MODIFIED
		#if CONFIG_AB_UPGRADE_LED
		extern int pwm_config(int, int, int);
		extern int pwm_enable(int);
		pwm_config(PWM_LED_GREEN_PGPIO, get_duty_ns(PWM_LED_SUCC_MODE_GREEN), get_period_ns(PWM_LED_SUCC_MODE_GREEN));
		pwm_config(PWM_LED_RED_PGPIO, get_duty_ns(PWM_LED_SUCC_MODE_RED), get_period_ns(PWM_LED_SUCC_MODE_RED));
		pwm_enable(PWM_LED_GREEN_PGPIO);
		pwm_enable(PWM_LED_RED_PGPIO);
		#endif
		#endif
		printf("%sUpdate successfully %s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);
	}

    #ifdef AB_MODIFIED
	#ifdef CONFIG_AB_UPGRADE_LED_SINGLE
	ab_fwup_led_toggle_deinit();
	#endif
	#endif
    
	/*
	 * To handle bootargs expanding for the kernel /proc/cmdline and uboot mtdids env setting
	 */
	sprintf(buf,"%s ",env_get("bootargs"));
	strcat(cmdline, buf);
	ret = nvt_part_config((char *)cmdline, NULL);
	if (ret < 0)
		return ret;

	env_set("bootargs",cmdline);

	return 0;
}

static int nvt_handle_fw_tbin(void)
{
	int ret = 0;
	char cmdline[1024] = {0};
	char buf[1024] = {0};

	ret = nvt_fw_load_tbin();
	if (ret < 0) {
		switch (ret) {
		case ERR_NVT_UPDATE_OPENFAILED:
			printf("Open SD fail:%s No SD device? (%s) %s\r\n", ANSI_COLOR_YELLOW, get_nvt_bin_name(NVT_BIN_NAME_TYPE_RUNFW), ANSI_COLOR_RESET);
			break;
		case ERR_NVT_UPDATE_NO_NEED:
		case ERR_NVT_UPDATE_READ_FAILED:
			printf("%sRead SD fail (%s) %s\r\n", ANSI_COLOR_RED, get_nvt_bin_name(NVT_BIN_NAME_TYPE_RUNFW), ANSI_COLOR_RESET);
			return -1;
			break;
		default:
			break;
		}
	} else
		printf("%sLoad successfully %s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);

	ret = nvt_fdt_init(false);
	if (ret < 0) {
		printf("modelext init fail\n");
		return ret;
	}

	/*
	 * To handle bootargs expanding for the kernel /proc/cmdline and uboot mtdids env setting
	 */
	sprintf(buf,"%s ",env_get("bootargs"));
	strcat(cmdline, buf);
	ret = nvt_part_config((char *)cmdline, NULL);
	if (ret < 0)
		return ret;

	env_set("bootargs",cmdline);

	return 0;
}

static int nvt_handle_fw_update_fw(void)
{
	int ret = 0;
	unsigned long val = 0;

	printf("%sStarting to update firmware runtime %s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);
	/* To handle firmware update */
	ret = nvt_fw_update(true);
	if (ret < 0) {
		printf("%sUpdate fail %s\r\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
		val = (nvt_readl((ulong)nvt_shminfo_comm_uboot_boot_func) & ~COMM_UBOOT_BOOT_FUNC_BOOT_DONE_MASK) | COMM_UBOOT_BOOT_FUNC_BOOT_NG;
		nvt_writel(val, (ulong)nvt_shminfo_comm_uboot_boot_func);
		flush_dcache_all();
		return -1;
	} else {
		printf("%sUpdate firmware successfully %s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);

		// Update finished.
		val = (nvt_readl((ulong)nvt_shminfo_comm_uboot_boot_func) & ~COMM_UBOOT_BOOT_FUNC_BOOT_DONE_MASK) | COMM_UBOOT_BOOT_FUNC_BOOT_DONE;
		nvt_writel(val, (ulong)nvt_shminfo_comm_uboot_boot_func);
		flush_dcache_all();
		while(1) {
			// Waiting for itron trigger reboot.
			printf(".");
			mdelay(1000);
		}
	}

	return 0;
}

static int nvt_handle_update_fw_by_usb_eth(void)
{
	int ret = 0;
	char cmdline[1024] = {0};
	char buf[1024] = {0};

	printf("%sStarting to update firmware from USB/ETH%s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);
	/* To handle firmware update */
	ret = nvt_fw_update(true);

    #ifdef AB_MODIFIED
	#ifdef CONFIG_FW_MULTI_PARTITION_ENABLE
	// a/b partition modify
	printf("[%s] Ability nvt_fw_update ret: %d\n",__func__,ret);
	u32 nand_addr = 0,nand_size = 0, nand_block_size = 0;
	int nodeoffset, len;
    u32 *pui_cell = NULL;
    char * pc_string_blk_size = NULL;
    /* get fdt info*/
    ulong fdt_addr = nvt_readl((ulong)nvt_shminfo_boot_fdt_addr);
    nodeoffset = fdt_path_offset((const void*)fdt_addr, "/nand/partition_ab_boot");
    if (nodeoffset < 0) {
        printf("%s(%d) nodeoffset < 0\n",__func__, __LINE__);
        return false;
    }
	
	pui_cell = (u32 *)fdt_getprop((const void*)fdt_addr, nodeoffset, "reg", &len);
    if (len == 0) {
        printf("%s(%d) len = 0\n",__func__, __LINE__);
        return false;
    }

    nand_addr = __be32_to_cpu(pui_cell[1]);
    nand_size = __be32_to_cpu(pui_cell[3]);
    printf("[%s] nand_addr: 0x%x, nand_size: 0x%x\n",__func__,nand_addr,nand_size);

    nodeoffset = fdt_path_offset((const void*)fdt_addr, "/nvt_info");
    if (nodeoffset < 0) {
        printf("%s(%d) nodeoffset < 0\n",__func__, __LINE__);
        return false;
    }

    pc_string_blk_size = (u32 *)fdt_getprop((const void*)fdt_addr, nodeoffset, "EMBMEM_BLK_SIZE", &len);
    if (len == 0) {
        printf("%s(%d) len = 0\n",__func__, __LINE__);
        return false;
    }
    printf("[%s] len: %d, pc_string_blk_size: %s\n",__func__,len,pc_string_blk_size);
    nand_block_size = simple_strtoul(pc_string_blk_size, NULL, 16);
	
	int boot_ret = 0;
	if(ret == 0){
        boot_ret = ab_reset_bootup_config(nand_addr,nand_size,nand_block_size);
        if(boot_ret<0)
            ab_err_print("reset boot config fail.\n");
    }else{
        if(ab_load_bootup_config(nand_addr,nand_size,nand_block_size) == 0)
            b_load_ab_boot = true;
        else
        {
            boot_ret = ab_reset_bootup_config(nand_addr,nand_size,nand_block_size);
            if(boot_ret<0)
                ab_err_print("reset boot config fail.\n");
        }
    }
    //  ab partition modify end
	#endif
    #endif
    
	if (ret < 0) {
		#ifdef AB_MODIFIED
		ab_err_print("%sUpdate fail %s\r\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
		#else
		printf("%sUpdate fail %s\r\n", ANSI_COLOR_RED, ANSI_COLOR_RESET);
		#endif
		return -1;
	} else {
		#ifdef AB_MODIFIED
		ab_err_print("%sUpdate firmware successfully %s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);
		#else
		printf("%sUpdate firmware successfully %s\r\n", ANSI_COLOR_YELLOW, ANSI_COLOR_RESET);
		#endif
	}

	/*
	 * To handle bootargs expanding for the kernel /proc/cmdline and uboot mtdids env setting
	 * Continue to boot
	 */
	sprintf(buf,"%s ",env_get("bootargs"));
	strcat(cmdline, buf);
	ret = nvt_part_config((char *)cmdline, NULL);
	if (ret < 0)
		return ret;

	env_set("bootargs",cmdline);
	return 0;
}

static int nvt_handle_format_rw_rootfs(void)
{
	int ret = 0;
	ret = nvt_process_rootfs_format();

	return ret;
}

static int nvt_handle_recovery_sys(void)
{
	int ret = 0;
	char cmdline[1024] = {0};
	char buf[1024] = {0};
	ret = nvt_process_sys_recovery();

	/*
	 * To handle bootargs expanding for the kernel /proc/cmdline and uboot mtdids env setting
	 */
	sprintf(buf,"%s ",env_get("bootargs"));
	strcat(cmdline, buf);
	ret = nvt_part_config((char *)cmdline, NULL);
	if (ret < 0)
		return ret;

	env_set("bootargs",cmdline);

	return ret;
}

#ifdef CONFIG_NVT_LINUX_AUTODETECT
static int nvt_handle_fw_auto(void)
{
	int ret = 0;
	unsigned long boot_reason = nvt_readl((ulong)nvt_shminfo_comm_uboot_boot_func) & COMM_UBOOT_BOOT_FUNC_BOOT_REASON_MASK;

	if (boot_reason == COMM_UBOOT_BOOT_FUNC_BOOT_UPDFIRM) {
		// Update by itron: firmware will be loaded by itron.
		ret = nvt_handle_fw_update_fw();
		if (ret < 0)
			return ret;
	} else if ((boot_reason == COMM_UBOOT_BOOT_FUNC_BOOT_UPD_FRM_USB) || \
				(boot_reason == COMM_UBOOT_BOOT_FUNC_BOOT_UPD_FRM_ETH)){
		// Update by USB/ETH: firmware will be loaded by loader.
		ret = nvt_handle_update_fw_by_usb_eth();
		if (ret < 0)
			return ret;
	} else if (nvt_detect_fw_tbin()) {
		ret = nvt_handle_fw_tbin();
		if (ret < 0)
			return ret;

		#ifndef CONFIG_NVT_LINUX_RAMDISK_SUPPORT
		char cmdline[512] = {0};
		char buf[255] = {0};
		/*
		 * To handle bootargs expanding for the kernel /proc/cmdline and uboot mtdids env setting
		 */
		sprintf(buf,"%s ",env_get("bootargs"));
		strcat(cmdline, buf);
		ret = nvt_part_config((char *)cmdline, NULL);
		if (ret < 0)
			return ret;

		env_set("bootargs",cmdline);
		#endif /* CONFIG_NVT_LINUX_RAMDISK_SUPPORT */
	} else if (boot_reason == COMM_UBOOT_BOOT_FUNC_BOOT_FORMAT_ROOTFS){
		// Format R/W rootfs, we should check if this partition is existed firstly.
		ret = nvt_handle_format_rw_rootfs();
		if (ret < 0)
			return ret;
	} else if (boot_reason == COMM_UBOOT_BOOT_FUNC_BOOT_RECOVERY_SYS){
		// Recovery system if loader send event to do system recovery. (EMMC boot only)
		ret = nvt_handle_recovery_sys();
		if (ret < 0)
			return ret;
	} else {
		ret = nvt_handle_fw_abin();
		if (ret < 0)
			return ret;
	}

	return 0;
}
#endif /* CONFIG_NVT_LINUX_AUTODETECT */
#endif /* CONFIG_NVT_IVOT_SOC_FW_UPDATE_SUPPORT */

#if defined(CONFIG_NVT_IVOT_EMMC)
static int nvt_emmc_init(void)
{
	int ret = 0;
	char command[128] = {0};

	/* Switch to emmc bus and user partition access config */
	sprintf(command, "mmc dev %d", CONFIG_NVT_IVOT_EMMC);
	ret = run_command(command, 0);
	if (ret < 0)
		return ret;
	sprintf(command, "mmc partconf %d 1 %d 0", CONFIG_NVT_IVOT_EMMC, CONFIG_NVT_IVOT_EMMC_BOOT_PART);
	ret = run_command(command, 0);

	return ret;
}
#elif defined(CONFIG_NVT_SPI_NOR)
static int nvt_norflash_init(void)
{
	int ret = 0;
	char command[128] = {0};

	sprintf(command, "sf probe");
	ret = run_command(command, 0);
	if (ret < 0) {
		nvt_dbg(ERR, "nor flash init failed\n");
	}

	return ret;
}
#endif /* CONFIG_NVT_LINUX_EMMC_BOOT */
/**
 * @brief misc_init_r - To do nvt update and board init.
 *
 * @return 0
 */
int misc_init_r(void)
{
	int ret = 0;

#ifdef CONFIG_NVT_IVOT_SOC_FW_UPDATE_SUPPORT
	nvt_dbg(FUNC, "%sFirmware name: %s %s %s %s \n", ANSI_COLOR_YELLOW,
						  get_nvt_bin_name(NVT_BIN_NAME_TYPE_FW),
						  get_nvt_bin_name(NVT_BIN_NAME_TYPE_RUNFW),
						  get_nvt_bin_name(NVT_BIN_NAME_TYPE_MODELEXT),
						  ANSI_COLOR_RESET);
	nvt_dbg(FUNC, "boot time: %lu(us) \n", get_nvt_timer0_cnt());
#endif /* CONFIG_NVT_IVOT_SOC_FW_UPDATE_SUPPORT */

#if defined(CONFIG_NVT_IVOT_EMMC)
	ret = nvt_emmc_init();
	if (ret < 0)
		return 0;
#elif defined(CONFIG_NVT_SPI_NOR)
	ret = nvt_norflash_init();
	if (ret < 0)
		return 0;
#endif /* CONFIG_NVT_LINUX_EMMC_BOOT */

#ifdef CONFIG_NVT_IVOT_SOC_FW_UPDATE_SUPPORT
	#ifdef CONFIG_NVT_LINUX_AUTODETECT
	ret = nvt_handle_fw_auto();
	if (ret < 0)
		return 0;
	#else /* !CONFIG_NVT_LINUX_AUTODETECT */
	#ifdef CONFIG_NVT_LINUX_SPINAND_BOOT
	ret = nvt_handle_fw_abin();
	if (ret < 0)
		return 0;
	#else /* !CONFIG_NVT_LINUX_SPINAND_BOOT */
	ret = nvt_handle_fw_tbin();
	if (ret < 0)
		return 0;
	#endif /* CONFIG_NVT_LINUX_SPINAND_BOOT */
	#endif /* CONFIG_NVT_LINUX_AUTODETECT */
#else /* !CONFIG_NVT_IVOT_SOC_FW_UPDATE_SUPPORT */
	/* FIXME: To do customized boot */
	ret = nvt_fdt_init(true);
	if (ret < 0) {
		printf("modelext init fail\n");
		return 0;
	}
#endif /* CONFIG_NVT_IVOT_SOC_FW_UPDATE_SUPPORT */

	/* Why to do this again?
	 * This is because modelext maybe updated, we should init again here.
	 */
	nvt_dbg(FUNC, "boot time: %lu(us) \n", get_nvt_timer0_cnt());
	ret = nvt_board_init();
	if (ret < 0) {
		printf("board init fail\n");
		return 0;
	}

	nvt_dbg(FUNC, "boot time: %lu(us) \n", get_nvt_timer0_cnt());

	return 0;
}

/*
 * dram init.
 */
int dram_init_banksize(void)
{
	gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
	gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

	return 0;
}

phys_size_t get_effective_memsize(void)
{
	/* We should report the uboot dram size due to relocate
	 * address will be calculated by this size to get ram top address,
	 * #ifdef CONFIG_SYS_SDRAM_BASE
	 *   gd->ram_base = CONFIG_SYS_SDRAM_BASE;
	 * #endif
	 *   gd->ram_top = gd->ram_base + get_effective_memsize();
	 *   gd->ram_top = board_get_usable_ram_top(gd->mon_len);
	 *   gd->relocaddr = gd->ram_top;
	 */
	return CONFIG_SYS_SDRAM_SIZE;
}

/*
 * get_board_rev() - get board revision
 */
u32 get_board_rev(void)
{
	return 0;
}

int board_late_init(void)
{
	return 0;
}
