/**************************************************************************//**
 * @file     main.c
 * @version  V1.00
 * $Revision: 1 $
 * $Date: 14/07/15 2:52p $
 * @brief    N572 General Purpose I/O Driver Sample Code
 *
 * @note
 * Copyright (C) 2014 Nuvoton Technology Corp. All rights reserved.
 *
 ******************************************************************************/
#include <stdio.h>
#include "N572.h"
#include "gpio.h"

#define TEST_PATTERN                0xA5A5A5A5

__align(512) const char u32RomSpace[1024*50];

void SYS_Init(void)
{
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init System Clock                                                                                       */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Unlock protected registers */
    SYS_UnlockReg();

    // Enable Internal RC
#if( __CHIP_SERIES__ == __N572F065_SERIES__ )	
    CLK_EnableXtalRC(CLK_PWRCTL_OSC24M_EN_Msk);
#endif
#if( __CHIP_SERIES__ == __N572F072_SERIES__ )	
    CLK_EnableXtalRC(CLK_PWRCTL_OSC48M_EN_Msk);
#endif
	
    // Enable External XTL32K
    CLK_EnableXtalRC(CLK_PWRCTL_XTL32K_EN_Msk);

		// Enable PLL1 & PLL2
		CLK_SetPLL1Mode(PLL_NORMAL_MODE);
		CLK_SetPLL2Mode(PLL_NORMAL_MODE);
	
		// Select PLL2 input source and frequence
#if( __CHIP_SERIES__ == __N572F065_SERIES__ )	
		CLK_SetPLL2(PLL2_SRC_PLL1_FOUT, NULL); // 32K -> PLL1(6M) -> PLL2(48M)
#endif
#if( __CHIP_SERIES__ == __N572F072_SERIES__ )	
		CLK_SetPLL2(PLL2_SRC_PLL1_FOUT, PLL2_48M);
#endif

		// Set RAM wait state
		SYS_SET_RAM_WAIT_STATE(SYS_IPRST0_RAMWS_ZERO_WAIT);
	
    // Set flash memory wait state
		SYS_SET_CPU_WAIT_STATE(SYS_IPRST0_CPUWS_1HCLK_WAIT);

		// Procedures to configure the HCLK from internal RC
#if( __CHIP_SERIES__ == __N572F065_SERIES__ )	
	    //CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_XTL_32K,CLK_CLKDIV_HCLK(1));
			//CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL1_FOUT,CLK_CLKDIV_HCLK(1));
			CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL2_FOUT,CLK_CLKDIV_HCLK(1));
			//CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_RC_24M,CLK_CLKDIV_HCLK(1));
#endif
#if( __CHIP_SERIES__ == __N572F072_SERIES__ )	
	    //CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_XTL_32K,CLK_CLKDIV_HCLK(1));
			//CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL1_FOUT,CLK_CLKDIV_HCLK(1));
			CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL2_FOUT,CLK_CLKDIV_HCLK(1));
			//CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_RC_48M,CLK_CLKDIV_HCLK(1));
		#endif

    // Update System Core Clock
    // User can use SystemCoreClockUpdate() to calculate SystemCoreClock.
    SystemCoreClockUpdate();

    // Lock protected registers
    SYS_LockReg();

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/
}

int32_t fill_data_pattern(uint32_t u32StartAddr, uint32_t u32EndAddr, uint32_t u32Pattern)
{
    uint32_t u32Addr;

    for (u32Addr = u32StartAddr; u32Addr < u32EndAddr; u32Addr += 4) {
        FMC_Write(u32Addr, u32Pattern);
    }
    return 0;
}

int32_t  verify_data(uint32_t u32StartAddr, uint32_t u32EndAddr, uint32_t u32Pattern)
{
    uint32_t    u32Addr;
    uint32_t    u32data;

    for (u32Addr = u32StartAddr; u32Addr < u32EndAddr; u32Addr += 4) {
        u32data = FMC_Read(u32Addr);
        if (u32data != u32Pattern) {
            printf("\nFMC_Read data verify failed at address 0x%x, read=0x%x, expect=0x%x\n", u32Addr, u32data, u32Pattern);
            return -1;
        }
    }
    return 0;
}

int32_t  flash_test(uint32_t u32StartAddr, uint32_t u32EndAddr, uint32_t u32Pattern)
{
    uint32_t    u32Addr;

    for (u32Addr = u32StartAddr; u32Addr < u32EndAddr; u32Addr += FMC_FLASH_PAGE_SIZE) {
        printf("    Flash test address: 0x%x    \r", u32Addr);

        // Erase page
        if (FMC_Erase(u32Addr) < 0) {
            printf("\nPage 0x%x erase failed!\n", u32Addr);
            return -1;
        }

        // Verify if page contents are all 0xFFFFFFFF
        if (verify_data(u32Addr, u32Addr + FMC_FLASH_PAGE_SIZE, 0xFFFFFFFF) < 0) {
            printf("\nPage 0x%x erase verify failed!\n", u32Addr);
            return -1;
        }

        // Write test pattern to fill the whole page
        if (fill_data_pattern(u32Addr, u32Addr + FMC_FLASH_PAGE_SIZE, u32Pattern) < 0) {
            printf("Failed to write page 0x%x!\n", u32Addr);
            FMC_Erase(u32Addr);
            return -1;
        }

        // Verify if page contents are all equal to test pattern
        if (verify_data(u32Addr, u32Addr + FMC_FLASH_PAGE_SIZE, u32Pattern) < 0) {
            printf("\nData verify failed!\n ");
            FMC_Erase(u32Addr);
            return -1;
        }

        // Erase page
        if (FMC_Erase(u32Addr) < 0) {
            printf("\nPage 0x%x erase failed!\n", u32Addr);
            return -1;
        }

        // Verify if page contents are all 0xFFFFFFFF
        if (verify_data(u32Addr, u32Addr + FMC_FLASH_PAGE_SIZE, 0xFFFFFFFF) < 0) {
            printf("\nPage 0x%x erase verify failed!\n", u32Addr);
            return -1;
        }
    }
    printf("\r    Flash Test Passed.          \n");
    return 0;
}

/*---------------------------------------------------------------------------------------------------------*/
/* MAIN function                                                                                           */
/*---------------------------------------------------------------------------------------------------------*/
int main (void)
{
    uint32_t u32Data, au32Config;

    /* Lock protected registers */
    if(SYS->REGLCTL == 1) // In end of main function, program issued CPU reset and write-protection will be disabled.
        SYS_LockReg();

		/* Init System, IP clock and multi-function I/O
       In the end of SYS_Init() will issue SYS_LockReg()
       to lock protected register. If user want to write
       protected register, please issue SYS_UnlockReg()
       to unlock protected register if necessary */
    SYS_Init();

    /* Enable FMC ISP function */
    SYS_UnlockReg();
    FMC_Open();

    /* Read Company ID */
    u32Data = FMC_ReadCID();
    if (u32Data != 0xda) {
        printf("Wrong CID: 0x%x\n", u32Data);
        goto lexit;
    }

    printf("\n\n");
    printf("+------------------------------------------------------------------------+\n");
    printf("|               Flash Memory Controller Driver Sample Code               |\n");
    printf("+------------------------------------------------------------------------+\n");
    printf("  NOTE: This sample must be applied to N572.\n");

    u32Data = FMC_ReadCID();
    printf("  Company ID ................................. [0x%08x]\n", u32Data);

    u32Data = FMC_ReadDID();
    printf("  Device ID .................................. [0x%08x]\n", u32Data);

    FMC_ReadConfig(&au32Config);
    printf("  User Config ................................ [0x%08x]\n", au32Config);

    printf("\n\nData Flash test =>\n");
    if (flash_test((uint32_t )&u32RomSpace[0], (uint32_t)&u32RomSpace[1024*50-1], TEST_PATTERN) < 0) {
        printf("\n\nUHB test failed!\n");
        goto lexit;
    }

lexit:

    /* Disable FMC ISP function */
    FMC_Close();

    /* Lock protected registers */
    SYS_LockReg();

    printf("\nFMC Sample Code Completed.\n");

    while (1);
}
