/**************************************************************************//**
 * @file     main.c
 * @brief    Load conprog.bin code from SD or NAND device for next booting stage
 *
 * SPDX-License-Identifier: Apache-2.0
 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "N9H20.h"
#include "nvtloader.h"
#ifdef USB_HOST
#include "usb.h"
#endif

#ifdef __MASS_PARODUCT__	
void	EMU_MassProduction(void);
#endif

extern void loadKernelCont(int fd, int offset);
extern void playAni(int fd, char* pcString);
//extern void mass(NDISK_T *disk);
void mass(NDISK_T *disk, INT32 i32TotalSector);
extern void lcmFill2Dark(unsigned char*);
extern void initVPostShowLogo(void);

#if defined(__GNUC__)
unsigned char kbuf[CP_SIZE] __attribute__((aligned (32))); /* save first 16k of buffer. Copy to 0 after vector table is no longer needed */
UINT8 dummy_buffer[512] __attribute__((aligned (32)));
#else
__align(32) unsigned char kbuf[CP_SIZE];
__align(32) UINT8 dummy_buffer[512];
#endif



extern void AudioChanelControl(void);
extern void backLightEnable(void);
int kfd, mfd;
BOOL bIsIceMode = FALSE;	
	
int g_ibr_boot_sd_port=-1;     // indicate the SD port number which IBR boot from.		

static NDRV_T _nandDiskDriver0 =
{
    nandInit0,
    nandpread0,
    nandpwrite0,
    nand_is_page_dirty0,
    nand_is_valid_block0,
    nand_ioctl,
    nand_block_erase0,
    nand_chip_erase0,
    0
};


static NDISK_T ptNDisk;
extern UINT16 u16Volume;
/* Volume config file locate in NAND disk */ 
void VolumeConfigFile(BOOL bIsFromNAND)
{
	INT8 path[64];
	/* Check if volume config file exists */
	if(bIsFromNAND==0)
		fsAsciiToUnicode(VOLUME_PATH, path, TRUE);
	else
		fsAsciiToUnicode(VOLUME_PATH_SD, path, TRUE);	
		
	kfd = fsOpenFile(path, 0, O_RDONLY);
	if(kfd > 0)
	{
		INT32		nStatus, nLen;		
		UINT8  		u8VolCfg[2];		
		nStatus = fsReadFile(kfd, u8VolCfg, 2, &nLen);
		if(nStatus>=0)
		{
			u16Volume = u8VolCfg[0]|(((UINT16)u8VolCfg[1])<<8); 
			u16Volume = u16Volume * 63/100;
			sysprintf("Volume = %d\n", u16Volume);	 
		}	
	}
}

BOOL udcDetection(void)
{  
	UINT32 volatile u32Delay = 0x100000;
	BOOL bIsUsbDet;	
	outp32(REG_AHBCLK, inp32(REG_AHBCLK) | USBD_CKE);
	outpw(REG_AHBIPRST, inpw(REG_AHBIPRST) | UDCRST);
	outpw(REG_AHBIPRST, inpw(REG_AHBIPRST) & ~UDCRST);	
	outp32(PHY_CTL, (0x20 | Phy_suspend));
	
	outp32(OPER, 0x0);		
	while(inp32(OPER) != 0x0);	
	while(u32Delay--);
	if(inp32(PHY_CTL) & Vbus_status) 
		bIsUsbDet = TRUE;
	else
		bIsUsbDet = FALSE;	
	outp32(REG_AHBCLK, inp32(REG_AHBCLK) & ~USBD_CKE);
	outpw(REG_AHBIPRST, inpw(REG_AHBIPRST) | UDCRST);
	outpw(REG_AHBIPRST, inpw(REG_AHBIPRST) & ~UDCRST);		
	return bIsUsbDet;
}

UINT32 u32TimerChannel = 0;
void Timer0_300msCallback(void)
{
#ifdef __BACKLIGHT__
	backLightEnable();
#endif	
	sysClearTimerEvent(TIMER0, u32TimerChannel);
}
void init(void)
{
	WB_UART_T 	uart;
	UINT32 		u32ExtFreq;	    	    	
	UINT32 u32Cke = inp32(REG_AHBCLK);
	
	/* Reset SIC engine to fix USB update kernel and mvoie file */
	outp32(REG_AHBCLK, u32Cke  | (SIC_CKE | NAND_CKE | SD_CKE)); 
	outp32(REG_AHBIPRST, inp32(REG_AHBIPRST )|SICRST ); 
	outp32(REG_AHBIPRST, 0); 
	
	outp32(REG_APBIPRST, TMR0RST | TMR1RST); 
	outp32(REG_APBIPRST, 0); 
	
	outp32(REG_AHBCLK,u32Cke);	
	sysEnableCache(CACHE_WRITE_BACK);
	
	/* init timer */	
	u32ExtFreq = sysGetExternalClock();    	/* KHz unit */	
	sysSetTimerReferenceClock (TIMER0, 
								u32ExtFreq*1000);	/* Hz unit */
	sysStartTimer(TIMER0, 
					100, 
					PERIODIC_MODE);
    u32TimerChannel = sysSetTimerEvent(TIMER0, 
    									30,
    									(PVOID)Timer0_300msCallback);
    
    /* enable UART */
    sysUartPort(1);
	uart.uiFreq = u32ExtFreq*1000;					/* Hz unit */	
	uart.uiBaudrate = 115200;
	uart.uiDataBits = WB_DATA_BITS_8;
	uart.uiStopBits = WB_STOP_BITS_1;
	uart.uiParity = WB_PARITY_NONE;
	uart.uiRxTriggerLevel = LEVEL_1_BYTE;
	sysInitializeUART(&uart);    
	sysprintf("NVT Loader start\n");
#ifndef __CC__
    kpi_init();
	kpi_open(3); // use nIRQ0 as external interrupt source
	bIsIceMode = kpi_read(KPI_NONBLOCK);
	if(bIsIceMode!=FALSE)
		bIsIceMode=TRUE;
#endif 	
	sysSetLocalInterrupt(ENABLE_IRQ);
			
}


typedef struct sd_info
{
	unsigned int startSector;
	unsigned int endSector;
	unsigned int fileLen;
	unsigned int executeAddr;
}NVT_SD_INFO_T;

unsigned char *buf;
unsigned int *pImageList;

/* read image information */
UINT32 ParsingReservedArea(void)
{
	UINT32 u32ReservedSector=0;
	NVT_SD_INFO_T image;
	int count, i;
	
	buf = (UINT8 *)((UINT32)dummy_buffer | 0x80000000);
	pImageList=((unsigned int *)(((unsigned int)dummy_buffer)|0x80000000));
#if 1
	if (g_ibr_boot_sd_port == 0){
		sicSdRead0(33, 1, (UINT32)dummy_buffer);	
	}else if (g_ibr_boot_sd_port == 1){
		sicSdRead1(33, 1, (UINT32)dummy_buffer);	
	}else if (g_ibr_boot_sd_port == 2){
		sicSdRead2(33, 1, (UINT32)dummy_buffer);	
	}
#else	
#ifdef __ENABLE_SD_CARD_0__
	sicSdRead0(33, 1, (UINT32)dummy_buffer);	
#endif	
#ifdef __ENABLE_SD_CARD_1__
	sicSdRead1(33, 1, (UINT32)dummy_buffer);	
#endif	
#endif
	pImageList=((unsigned int *)(((unsigned int)dummy_buffer)|0x80000000));
	if( (*(pImageList+2)) !=0xFFFFFFFF)
	{
		sysprintf("Turbo writter reserved area %dKB\n",u32ReservedSector/2); 
		u32ReservedSector = (*(pImageList+2));
	}	
	if (((*(pImageList+0)) == 0x574255aa) && ((*(pImageList+3)) == 0x57425963))
	{
		count = *(pImageList+1);

		pImageList = pImageList+4;
		for (i=0; i<count; i++)
		{
			if (((*(pImageList) >> 16) & 0xffff) < 4)
			{
				image.startSector = *(pImageList + 1) & 0xffff;
				image.endSector = (*(pImageList + 1) & 0xffff0000) >> 16;
				if(image.endSector>u32ReservedSector)
					u32ReservedSector = image.endSector;
				image.executeAddr = *(pImageList + 2);
				image.fileLen = *(pImageList + 3);			
			}
			/* pointer to next image */
			pImageList = pImageList+12;
		}
	}
	return (u32ReservedSector+1);
}	

UINT32 NVT_LoadKernelFromSD(void)
{
	INT8 path[64];
	INT32 i32ErrorCode;
	INT found_kernel = 0;
	INT found_avi = 0;
	PDISK_T		*pDiskList;
	UINT32 block_size, free_size, disk_size; 
	UINT32 u32TotalSize;
	INT32 i32BootSDTotalSector;	
	void	(*_jump)(void);
	
	DBG_PRINTF("Loader will load conprog.bin in SD card.\n");
	fsAssignDriveNumber('X', DISK_TYPE_SD_MMC, 0, 1); 
	fsAssignDriveNumber('Y', DISK_TYPE_SD_MMC, 0, 2);
	     
	
	/*-----------------------------------------------------------------------*/
	/*  Init SD card                                                         */
	/*-----------------------------------------------------------------------*/
	sicOpen();
	sicIoctl(SIC_SET_CLOCK, 192000, 0, 0);
	
#if 1	
	if (g_ibr_boot_sd_port == 0){
		sysprintf("Load code from SD0\n");
		i32BootSDTotalSector = sicSdOpen0();	/* Total sector or error code */
		if(i32BootSDTotalSector < 0){
	        	sicSdClose0();
	        	exit(1);
	        }	
		sysprintf("total SD0 sectors number (%x)\n", i32BootSDTotalSector);
	}
	else if (g_ibr_boot_sd_port == 1){
		sysprintf("Load code from SD1\n");
		i32BootSDTotalSector = sicSdOpen1();	/* Total sector or error code */
		if(i32BootSDTotalSector < 0){
	   	     sicSdClose1();
	   	     exit(1);
	   	}
		sysprintf("total SD1 sectors (%x)\n", i32BootSDTotalSector);
	}
	else if (g_ibr_boot_sd_port == 2){
		sysprintf("Load code from SD2\n");
		i32BootSDTotalSector = sicSdOpen2();	/* Total sector or error code */
		if(i32BootSDTotalSector < 0){
	  	      sicSdClose2();
	  	      exit(1);
	  	} 
		sysprintf("total SD2 sectors (%x)\n", i32BootSDTotalSector);
	}
#else		
#ifdef __ENABLE_SD_CARD_0__ 	
	sysprintf("Load code from SD 0\n");	
	i32ErrorCode = sicSdOpen0();	/* Total sector or error code */
	sysprintf("total SD 0 sectors (%x)\n", sicSdOpen0());	
#endif 	
#ifdef __ENABLE_SD_CARD_1__ 	
	sysprintf("Load code from SD 1\n");	
	i32ErrorCode = sicSdOpen1();	/* Total sector or error code */
	sysprintf("total SD 1 sectors (%x)\n", sicSdOpen1());	
#endif
#endif	
	/* In here for USB VBus stable. Othwise, USB library can not judge VBus correct  */ 
	sysprintf("UDC open\n");
	udcOpen();	 
	
	/*? DBG_PRINTF("total SD sectors (%x)\n", sicSdOpen());	?*/
	
	/* Get SD disk information*/ 
	pDiskList = fsGetFullDiskInfomation();
	sysprintf("Total Disk Size = %dMB\n", pDiskList->uDiskSize/1024);
	/* Format NAND if necessery */
	if ((fsDiskFreeSpace('X', &block_size, &free_size, &disk_size) < 0) || 
	    (fsDiskFreeSpace('Y', &block_size, &free_size, &disk_size) < 0))
	{   
		UINT32 u32Reserved;
		u32Reserved = ParsingReservedArea();
		sysprintf("unknow disk type, format device ...Reserved Area= %dKB \n", u32Reserved/2);
		fsSetReservedArea(u32Reserved);	

	#if 1
		u32TotalSize = (i32ErrorCode-u32Reserved)*512; 
	#endif 	
				
    	if (fsTwoPartAndFormatAll((PDISK_T *)pDiskList->ptSelf, SD1_1_SIZE*1024, (u32TotalSize- SD1_1_SIZE*1024)) < 0) 
    	{
			sysprintf("Format failed\n");					
			goto sd_halt;
		}
    	fsSetVolumeLabel('X', "SD1-1\n", strlen("SD1-1"));
		fsSetVolumeLabel('Y', "SD1-2\n", strlen("SD1-2"));	
	}			
	/* Read volume config file */ 
	VolumeConfigFile(1);		/* Read volume config file from SD */
	/* Detect USB */ 
	if(udcIsAttached())
	{
		//for mass's issue. sicSdClose();
		sysprintf("Detect USB plug in\n");		
		//mass(&ptNDisk, i32ErrorCode);				/* ptNDisk is useless for SD mass-storage*/
	  mass(&ptNDisk, i32BootSDTotalSector);					/* ptNDisk is useless for SD mass-storage*/
		sysprintf("USB plug out\n");
	}				
#ifdef __WIFI_NO_LCM__	 /* Make sure ADO, VPOST and SPU clock has been turn off */	
	outp32(REG_AHBCLK, inp32(REG_AHBCLK) & ~(ADO_CKE|VPOST_CKE|SPU_CKE));
#else		
	// Check if movie & Kernel exists.
	fsAsciiToUnicode(MOVIE_PATH_SD, path, TRUE);
	mfd = fsOpenFile(path, 0, O_RDONLY);
	if(mfd > 0) 
	{
		found_avi = 1;
		fsCloseFile(mfd);
		sysprintf("animation file found\n");
	}
#endif	
	fsAsciiToUnicode(KERNEL_PATH_SD, path, TRUE);
	kfd = fsOpenFile(path, 0, O_RDONLY);
	if(kfd > 0) 
	{			
		found_kernel = 1;
		sysprintf("kernel found\n");
	}	
	
	#if defined(__IXL_WINTEK__) || defined(__GWMT9360A__) || defined(__GWMT9615A__)	
	AudioChanelControl();
	#endif

#ifdef __WIFI_NO_LCM__	

#else	
	/* Initial SPU in advance for linux set volume issue */ 
#if defined(N9H20K3) || defined(N9H20K5)		
	spuOpen(eDRVSPU_FREQ_8000);
	//spuDacOn(1);
	spuIoctl(SPU_IOCTL_SET_VOLUME, u16Volume, u16Volume);
#endif
#endif

#ifdef __BAT_DET__
	BatteryDetection(FALSE);
#endif		
	if(found_avi) 
	{ 
#ifdef __WIFI_NO_LCM__	

#else						
  #if defined(N9H20K3) || defined(N9H20K5)	
		char ucSring[64]= MOVIE_PATH_SD;
		playAni(kfd, ucSring);
  #endif	
#endif	
	} 
	else 
	{
		if(found_kernel)				
			loadKernelCont(kfd, 0);
	}
	
	if(kfd > 0)
	{	
		unsigned int i = 0;
		fsCloseFile(kfd);	//Close kernel file 
#if 1
	        if (g_ibr_boot_sd_port == 0)
	        	sicSdClose0();
	        else if (g_ibr_boot_sd_port == 1)
	        	sicSdClose1();
	        else if (g_ibr_boot_sd_port == 2)
	        	sicSdClose2();
		sicClose();
#else				
#ifdef __ENABLE_SD_CARD_0__ 		
		sicSdClose0();
#endif
#ifdef __ENABLE_SD_CARD_1__ 		
		sicSdClose1();
#endif		
		sicClose();
#endif
		sysSetGlobalInterrupt(DISABLE_ALL_INTERRUPTS);
		sysSetLocalInterrupt(DISABLE_FIQ_IRQ);		
		// Invalid and disable cache
		sysDisableCache();
		sysInvalidCache();
				
		memcpy((unsigned char *)i, kbuf, CP_SIZE);
		
		// JUMP to kernel
		sysprintf("Jump to kernel\n");
		//lcmFill2Dark((char *)(FB_ADDR | 0x80000000));	
		outp32(REG_AHBIPRST, JPGRST | SICRST |UDCRST | EDMARST);
		outp32(REG_AHBIPRST, 0);
		outp32(REG_APBIPRST, UART1RST | UART0RST | TMR1RST | TMR0RST );
		outp32(REG_APBIPRST, 0);
		
		sysFlushCache(I_D_CACHE);	   	
		_jump = (void(*)(void))(0x0); // Jump to 0x0 and execute kernel
		_jump();	
		
		while(1);
		//return(0); // avoid compilation warning
	}
	else
	{
		DBG_PRINTF("Cannot find conprog.bin in SD card.(err=0x%x)\n", kfd);
		DBG_PRINTF("Try load conprog.bin in NAND\n\n");
	}
	return Successful;
sd_halt:	
	sysprintf("systen exit\n");
	while(1); // never return		
}	

UINT32 NVT_LoadKernelFromNAND(void)
{
	INT found_kernel = 0;
	INT found_avi = 0;
	UINT32 block_size, free_size, disk_size; 
	UINT32 u32TotalSize;
	void	(*_jump)(void);
	INT8 path[64];

    fsAssignDriveNumber('C', DISK_TYPE_SMART_MEDIA, 0, 1);
    fsAssignDriveNumber('D', DISK_TYPE_SMART_MEDIA, 0, 2);                      
	     	
	
   	/* For detect VBUS stable */ 
   	sicOpen();
    sicIoctl(SIC_SET_CLOCK, 192000, 0, 0);  /* clock from PLL */   	
	
	/* In here for USB VBus stable. Othwise, USB library can not judge VBus correct  */ 	
#if 1
	udcOpen();	 
#endif	  	
	/* Initialize GNAND */
	if(GNAND_InitNAND(&_nandDiskDriver0, &ptNDisk, TRUE) < 0) 
	{
		sysprintf("GNAND_InitNAND error\n");
		goto halt;
	}	
	
	if(GNAND_MountNandDisk(&ptNDisk) < 0) 
	{
		sysprintf("GNAND_MountNandDisk error\n");
		goto halt;		
	}
	
	/* Get NAND disk information*/ 
	u32TotalSize = ptNDisk.nZone* ptNDisk.nLBPerZone*ptNDisk.nPagePerBlock*ptNDisk.nPageSize;
	sysprintf("Total Disk Size %d\n", u32TotalSize);	
	/* Format NAND if necessery */
	if ((fsDiskFreeSpace('C', &block_size, &free_size, &disk_size) < 0) || 
	    (fsDiskFreeSpace('D', &block_size, &free_size, &disk_size) < 0)) 			    
	    	{   
	    		sysprintf("unknow disk type, format device .....\n");	
		    	if (fsTwoPartAndFormatAll((PDISK_T *)ptNDisk.pDisk, NAND1_1_SIZE*1024, (u32TotalSize- NAND1_1_SIZE*1024)) < 0) {
					sysprintf("Format failed\n");					
				goto halt;
	    	}
	    	fsSetVolumeLabel('C', "NAND1-1\n", strlen("NAND1-1"));
			fsSetVolumeLabel('D', "NAND1-2\n", strlen("NAND1-2"));	
	}
	
	/* Detect USB */ 
	if(udcIsAttached())
	{
		sysprintf("USB plug in\n");		
		mass(&ptNDisk, FMI_NO_SD_CARD);	/* Total sector in ptNDisk. parameter 2 is useless for NAND*/
		sysprintf("USB plug out\n");
	}				
	/* Read volume config file to same as linux kernel */ 
	VolumeConfigFile(0);			/* Read volume config file from NAND */
	
#ifdef __WIFI_NO_LCM__	 /* Make sure ADO, VPOST and SPU clock has been turn off */	
	outp32(REG_AHBCLK, inp32(REG_AHBCLK) & ~(ADO_CKE|VPOST_CKE|SPU_CKE));
#else
	/* Check if movie & Kernel exists */
	sysprintf("%s\n",MOVIE_PATH);
	fsAsciiToUnicode(MOVIE_PATH, path, TRUE);
	kfd = fsOpenFile(path, 0, O_RDONLY);
	if(kfd > 0) 
	{
		found_avi = 1;
		fsCloseFile(kfd);
		sysprintf("animation file found\n");
	}
#endif	
	fsAsciiToUnicode(KERNEL_PATH, path, TRUE);
	kfd = fsOpenFile(path, 0, O_RDONLY);
	if(kfd > 0) 
	{			
		found_kernel = 1;
		sysprintf("kernel found\n");
	}	
	
	#if defined(__IXL_WINTEK__) || defined(__GWMT9360A__) || defined(__GWMT9615A__)
	AudioChanelControl();
	#endif	
#ifdef __WIFI_NO_LCM__	

#else	
	/* Initial SPU in advance for linux set volume issue */ 	
	spuOpen(eDRVSPU_FREQ_8000);
	//spuDacOn(1);
	spuIoctl(SPU_IOCTL_SET_VOLUME, u16Volume, u16Volume);	
#endif		
#ifdef __BAT_DET__
	BatteryDetection(FALSE);
#endif	
	if(found_avi) 
	{ 
#ifdef __WIFI_NO_LCM__	

#else	
#if defined(N9H20K3) || defined(N9H20K5)
		char ucSring[64]= MOVIE_PATH;				
		playAni(kfd, ucSring);
#endif		
#endif	
	} 
	else 
	{
		if(found_kernel)				
			loadKernelCont(kfd, 0);
	}

	if(found_kernel) 
	{
		unsigned int i = 0;
		GNAND_UnMountNandDisk(&ptNDisk);
       	fmiSMClose(0);		
#ifdef __ENABLE_SD_CARD_0__		
		sicSdClose0();
#endif
#ifdef __ENABLE_SD_CARD_1__		
		sicSdClose1();
#endif					
		sicClose();
		/* Disable interrupt */
		sysSetGlobalInterrupt(DISABLE_ALL_INTERRUPTS);
		sysSetLocalInterrupt(DISABLE_FIQ_IRQ);		
		/* Invalid and disable cache */
		sysDisableCache();
		sysInvalidCache();
		
		/* Reset IPs */	
		sysprintf("Jump to kernel\n");
		//outp32(REG_AHBIPRST, JPGRST | SICRST |UDCRST );		
		outp32(REG_AHBIPRST, JPGRST | SICRST |UDCRST | EDMARST);
		outp32(REG_AHBIPRST, 0);
		outp32(REG_APBIPRST, UART1RST | UART0RST | TMR1RST | TMR0RST );
		outp32(REG_APBIPRST, 0);				
			
		memcpy((unsigned char *)i, kbuf, CP_SIZE);
    	sysFlushCache(I_D_CACHE);	   
     
		_jump = (void(*)(void))(0x0); /* Jump to 0x0 and execute kernel */
		_jump();	
	} 
	else 
	{
		sysprintf("Cannot find conprog.bin");
	}	
halt:
	sysprintf("systen exit\n");
	while(1); // never return
}



int main(void)
{
	// IBR and SDLoader keep the booting SD port number on register SDCR.
	// NVTLoader should load image from same SD port.
#if defined(__ENABLE_SD_CARD_0__)||defined(__ENABLE_SD_CARD_1__)||defined(__ENABLE_SD_CARD_2__)		
	outpw(REG_AHBCLK, inpw(REG_AHBCLK) | SD_CKE);   // SDLoader disable SIC/SD clock before jump to NVTLoader.
	outpw(REG_AHBCLK, inpw(REG_AHBCLK) | SIC_CKE);  // Now, enable clock to read SD register.
	g_ibr_boot_sd_port = (inpw(REG_SDCR) & SDCR_SDPORT) >> 29;
	outpw(REG_AHBCLK, inpw(REG_AHBCLK) & ~SD_CKE);
	outpw(REG_AHBCLK, inpw(REG_AHBCLK) & ~SIC_CKE);
#endif	
	
	sysDisableCache(); 	
	sysFlushCache(I_D_CACHE);		
#ifdef __BAT_DET__
	adc_init();
	adc_open(ADC_TS_4WIRE, 320, 240);
	BatteryDetection(FALSE);
#endif	
#ifdef __WIFI_NO_LCM__

#else	
#if defined(N9H20K3) || defined(N9H20K5)
	initVPostShowLogo();
#endif	
#endif
	init();			
			
	fsInitFileSystem();			
						
#if defined(__ENABLE_SD_CARD_0__)||defined(__ENABLE_SD_CARD_1__)||defined(__ENABLE_SD_CARD_2__)	
	sysprintf("NVT Loader: g_ibr_boot_sd_port = %d\n", g_ibr_boot_sd_port);
	NVT_LoadKernelFromSD();
#endif
	sysprintf("Load code from NAND\n");
	NVT_LoadKernelFromNAND();
	
	return(0); // avoid compilation warning
}