/**************************************************************************//**
 * @file     main.c
 * @version  V1.00
 * $Date:    19/11/12 16:42 $
 * @brief
 * @note
 * Copyright (C) 2019 Panchip Technology Corp. All rights reserved.
 *
 ******************************************************************************/
#include "pan271x.h"

#define __ramfunc __attribute__((section(".ramcode")))

uint32_t verify_debug_key1, verify_debug_key2, verify_debug_key3, verify_debug_key4;
uint8_t dbg_key_in_efuse[8];
uint8_t main_area_256b_at_0[256];

void Clock_Init(void)
{
	PMU->SYS_CLK_CTRL |= SYS_CLK_CTRL_OTP_READ_MODE_SEL_Msk;
	PMU->SYS_CLK_CTRL &= ~(SYS_CLK_CTRL_OTP_CLK_HIGH_CYCLE_Msk | SYS_CLK_CTRL_OTP_CLK_LOW_CYCLE_Msk);
	PMU->SYS_CLK_CTRL |= ((0X1 << SYS_CLK_CTRL_OTP_CLK_HIGH_CYCLE_Pos) | (0x0 << SYS_CLK_CTRL_OTP_CLK_LOW_CYCLE_Pos));
	PMU->SYS_CLK_CTRL |= SYS_CLK_CTRL_OTP_SW_UP_CYCLE_EN_Msk;
	#ifndef FPGA_MODE
	
	PMU->ANA_ANALDO |= ANA_ANALDO_EN_LDO_FSYN_AON_Msk;
    //MCU
	CLK_XthStartupConfig();
	PMU->XTH_CTRL |= XTH_CTRL_FSYNXO_BUFEN_AON_Msk;
    PMU->XTH_CTRL |= XTH_CTRL_FSYNXO_EN_AON_Msk;
	CLK_WaitClockReady(CLK_SYS_SRCSEL_XTH);
	
	CLK_HCLKConfig(0);
	CLK_SYSCLKConfig(CLK_DPLL_REF_CLKSEL_XTH,CLK_DPLL_OUT_48M); 
	CLK_RefClkSrcConfig(CLK_SYS_SRCSEL_DPLL_1);
	#endif

	CLK_AHBPeriphClockCmd(CLK_AHBPeriph_All,ENABLE);
	CLK_APB1PeriphClockCmd(CLK_APB1Periph_All,ENABLE);
}


void Sys_Init(void)
{
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Init I/O Multi-function  */
	SYS_SET_MFP(P1, 1, UART0_TX);
	SYS_SET_MFP(P1, 2, UART0_RX);
	GPIO_EnableDigitalPath(P1, BIT2);

	UART_Init(UART0, 115200);
    SYS_LockReg();
}


__ramfunc void TempFunc_ReadInit(uint8_t Ptm, bool ecc_en)
{
	uint32_t tmpreg;

	OTP->MODE_CTL = WORK_MODE_IDLE;
	OTP->READ_PROG_CTL = (OTP->READ_PROG_CTL & ~(0X1F << 4)) | (Ptm << 4);
	tmpreg = OTP->MODE_CTL;
	tmpreg = (tmpreg & ~WORK_MODE_IDLE) | WORK_MODE_READ;
	OTP->MODE_CTL = tmpreg;
	(ecc_en) ? (OTP->READ_PROG_CTL &= ~READ_PROG_CTL_ECC_DISABLE_Msk) : (OTP->READ_PROG_CTL |= READ_PROG_CTL_ECC_DISABLE_Msk);
}

__ramfunc void TempFunc_WriteInit(uint8_t Ptm, bool ecc_en)
{
	OTP->MODE_CTL = WORK_MODE_IDLE;
	OTP->READ_PROG_CTL = (OTP->READ_PROG_CTL & ~(0X1F << 4)) | (Ptm << 4);
	OTP->MODE_CTL = (OTP->MODE_CTL & ~WORK_MODE_IDLE) | WORK_MODE_WRITE;
	(ecc_en) ? (OTP->READ_PROG_CTL &= ~READ_PROG_CTL_ECC_DISABLE_Msk) : (OTP->READ_PROG_CTL |= READ_PROG_CTL_ECC_DISABLE_Msk);
}

__ramfunc void TempFunc_WriteWord(uint32_t Addr, uint8_t *buf)
{
	uint32_t w_data;

	OTP->BYTE_ADDR = Addr;
	w_data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);
	OTP->OPERATE_DATA_0 = w_data;
	OTP->OPERATE_TRG |= 0X1;
	while(OTP->OPERATE_TRG) {}
	buf+=4;
}

__ramfunc int8_t TempFunc_WriteStream(uint32_t adr, 
										uint32_t sz, 
										uint8_t *buf, 
										uint8_t WritePtm,
										uint8_t ReadPtm, 
										bool ecc_en) 
{
	uint32_t idx = 0;

//	((uint8_t*)(&adr))[3] = 0x02;
	TempFunc_WriteInit(WritePtm, ecc_en);

	for(idx = 0; idx < sz; idx +=4) 
	{
		TempFunc_WriteWord(adr+idx, buf+idx);
	}
	
	/* delay for exchange write/read mode */
	for(idx = 0; idx < SystemCoreClock/2; idx++){
		;
	}
	TempFunc_ReadInit(ReadPtm, ecc_en);
	return 0;
}

void data_printf(uint8_t *data, uint32_t len)
{
	uint32_t i = 0;

	for (i = 0; i < len; i++) 
	{
		SYS_TEST("0x%02X ", data[i]);
	}
	SYS_TEST("\n");
}

void DbgProtPrintSampleInfo(void)
{
    SYS_TEST("\n");
    SYS_TEST("+--------------------------------------------------------------------+ \n");
    SYS_TEST("|               PAN271 Debug Protect Sample Code.                    | \n");
    SYS_TEST("+--------------------------------------------------------------------+ \n");
    SYS_TEST("|    Press key to start specific testcase:                           | \n");
    SYS_TEST("|                                                                    | \n");
    SYS_TEST("|    Input '0'    Testcase 0: Generate Debug Key.                    | \n");
    SYS_TEST("|    Input '1'    Testcase 1: Write Generated Debug Key to Row Area. | \n");
    SYS_TEST("|    Input '2'    Testcase 2: Apply debug key to enable SWD Debug.   | \n");
    SYS_TEST("|    Input '3'    Testcase 3: Clear debug key to disable SWD Debug.  | \n");
    SYS_TEST("|    Input '4'    Testcase 4: Inject protect.                        | \n");
    SYS_TEST("+--------------------------------------------------------------------+ \n");
}

void GenerateDebugKey(void)
{
#if 0
    srand(300);
    verify_debug_key1 = (uint32_t)rand();
    srand(21);
    verify_debug_key2 = (uint32_t)rand();
    srand(4952);
    verify_debug_key3 = (uint32_t)rand();
    srand(569);
    verify_debug_key4 = (uint32_t)rand();
#else
    verify_debug_key1 = 0x0a320bfa;
    verify_debug_key2 = 0x32a24f95;
    verify_debug_key3 = 0x29fe9e58;
    verify_debug_key4 = 0x18e2403f;
#endif
    dbg_key_in_efuse[4] = (uint8_t)(verify_debug_key1 & 0x000000FFu);
    dbg_key_in_efuse[5] = (uint8_t)((verify_debug_key1 & 0xFF000000u) >> 24);
    dbg_key_in_efuse[6] = (uint8_t)(~((verify_debug_key2 & 0x0000FF00u) >> 8));
    dbg_key_in_efuse[7] = (uint8_t)((verify_debug_key1 & 0xFF000000u) >> 24);
    dbg_key_in_efuse[0] = (uint8_t)(verify_debug_key3 & 0x000000FFu);
    dbg_key_in_efuse[1] = (uint8_t)((verify_debug_key3 & 0x0000FF00u) >> 8);
    dbg_key_in_efuse[2] = (uint8_t)(~((verify_debug_key3 & 0x00FF0000u) >> 16));
    dbg_key_in_efuse[3] = (uint8_t)((verify_debug_key4 & 0x00FF0000u) >> 16);

    SYS_TEST("Debug Key in Registers: 0x%08x 0x%08x 0x%08x 0x%08x\n", verify_debug_key1, verify_debug_key2, verify_debug_key3, verify_debug_key4);
    SYS_TEST("Debug Key in eFuse: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
        dbg_key_in_efuse[0], dbg_key_in_efuse[1], dbg_key_in_efuse[2], dbg_key_in_efuse[3], dbg_key_in_efuse[4], dbg_key_in_efuse[5], dbg_key_in_efuse[6], dbg_key_in_efuse[7]);
}

__ramfunc void DumpKeysAndSecureCtrlFlag()
{
	uint8_t read_buf[32];

	TempFunc_ReadInit(PTM_TEST_ROW_READ, DISABLE);
	OTP_ReadStream(0x0, 32, read_buf);
	data_printf(read_buf, 32);
	OTP_ReadInit(PTM_USER_MAIN_AREA_READ, DISABLE);
}

__ramfunc void WriteDebugKeyToOtp(void)
{
    uint32_t secure_enable_addr = 0xc;
    uint8_t secure_data[4] = {0x00, 0x00, 0x00, 0x80};  // Set BIT2
	uint8_t read_buf[8];

	SYS_TEST("\n write row area with program cmd, ecc disabled \n");
	TempFunc_WriteStream(0x0, 8, dbg_key_in_efuse, PTM_TEST_ROW_PROGRAM, PTM_USER_MAIN_AREA_READ, DISABLE);
	SYS_TEST("\n enable debug switch \n");
	TempFunc_WriteStream(secure_enable_addr, 4, secure_data, PTM_TEST_ROW_PROGRAM, PTM_USER_MAIN_AREA_READ, DISABLE);
	SYS_TEST("\n read row area \n");
	OTP_ReadInit(PTM_TEST_ROW_READ, DISABLE);
	OTP_ReadStream(0x0, 16, read_buf);
	data_printf(read_buf, 16);

	

    // Do chip reset to apply the new configuration
    SYS_TEST("\nTrigger reset to apply the new configuration..\n");
//	TempFunc_WriteStream(0, 256, main_area_256b_at_0, PTM_USER_MAIN_AREA_PROGRAM, PTM_USER_MAIN_AREA_READ, DISABLE);
//    SYS_delay_10nop(0x1000);    // Delay to wait uart print done.
	SYS_UnlockReg();
	CLK->IPRST0 |= IPRST0_CHIPRST_Msk;
    while (1) {}
}

void ApplyDebugKeyToReg(void)
{
    // Write Debug Key to Register
    SECURE->VERIFI_DEBUG_KEY0 = verify_debug_key1;
    SECURE->VERIFI_DEBUG_KEY1 = verify_debug_key2;
    SECURE->VERIFI_DEBUG_KEY2 = verify_debug_key3;
    SECURE->VERIFI_DEBUG_KEY3 = verify_debug_key4;

    SYS_TEST("Debug Key Applied to Enable SWD Debug: 0x%08x 0x%08x 0x%08x 0x%08x\n",
        SECURE->VERIFI_DEBUG_KEY0,
		SECURE->VERIFI_DEBUG_KEY1,
		SECURE->VERIFI_DEBUG_KEY2,
		SECURE->VERIFI_DEBUG_KEY3);
}

void ClearDebugKeyInReg(void)
{
    // Clear Debug Key in Register
    SECURE->VERIFI_DEBUG_KEY0 = 0;
    SECURE->VERIFI_DEBUG_KEY1 = 0;
    SECURE->VERIFI_DEBUG_KEY2 = 0;
    SECURE->VERIFI_DEBUG_KEY3 = 0;

    SYS_TEST("Debug Key Cleared to 0 to Disable SWD Debug\n");
}

__ramfunc void InjectProtect(void)
{
    uint32_t secure_enable_addr = 0x8, inject_protect_addr = 0x3000;
	/* set protect offset 0x3000, offset = 0x3000 / 256 = 48 */
    uint8_t secure_data[8] = {0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x40};  // Set BIT30
	uint8_t read_buf[16];
	uint8_t write_buf[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
							 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
							};
	/* read inject protect area */
	SYS_TEST("\n direct read inject protect area \n");
	for (uint8_t i = 0; i < 16; i+=4) 
	{
		*(uint32_t *)(read_buf+i) = (*(uint32_t *)(inject_protect_addr+i));
		SYS_TEST(" %04x ", *(uint32_t *)(read_buf+i));
	}

	SYS_TEST("\n indirect read inject protect area \n");
	memset(read_buf, 0, 16);
	OTP_ReadInit(PTM_USER_MAIN_AREA_READ, DISABLE);
	OTP_ReadStream(inject_protect_addr, 16, read_buf);
	for (uint8_t i = 0; i < 16; i+=4) 
	{
		SYS_TEST(" %04x ", *(uint32_t *)(read_buf+i));
	}
	
	SYS_TEST("\n write main area 0x3000 with program cmd, ecc disabled \n");
	TempFunc_WriteStream(inject_protect_addr, 16, write_buf, PTM_USER_MAIN_AREA_PROGRAM, PTM_USER_MAIN_AREA_READ, DISABLE);
	SYS_TEST("\n read main area \n");
	OTP_ReadInit(PTM_USER_MAIN_AREA_READ, DISABLE);
	OTP_ReadStream(inject_protect_addr, 16, read_buf);
	for (uint8_t i = 0; i < 16; i++) 
	{
		SYS_TEST(" %02x ", read_buf[i]);
	}
	
	SYS_TEST("\n write row area with program cmd, ecc disabled, enable inject protect \n");
	TempFunc_WriteStream(secure_enable_addr, 8, secure_data, PTM_TEST_ROW_PROGRAM, PTM_USER_MAIN_AREA_READ, DISABLE);
	SYS_TEST("\n read row area \n");
	OTP_ReadInit(PTM_TEST_ROW_READ, DISABLE);
	OTP_ReadStream(secure_enable_addr, 8, read_buf);
	for (uint8_t i = 0; i < 8; i++) 
	{
		SYS_TEST(" %02x ", read_buf[i]);
	}

    SYS_TEST("\nTrigger reset to apply the new configuration..\n");
	SYS_UnlockReg();
	CLK->IPRST0 |= IPRST0_CHIPRST_Msk;
    while (1) {}
}

/*------------------
    Main function
 ------------------*/
int main(void)
{
    char c;

    Clock_Init();
	CLK->IPRST1 |= IPRST1_UART0RST_Msk;
	CLK->IPRST1 &= ~IPRST1_UART0RST_Msk;
    Sys_Init();

    SYS_TEST("\nCPU @ %dHz\n", SystemCoreClock);

    if (CLK->RSTSTS & RSTSTS_CHIP0RF_Msk)
    {
        CLK->RSTSTS = CLK->RSTSTS;  // Clear reset flags
    }

    SYS_TEST("\n Row Area content after reset:\n");
    DumpKeysAndSecureCtrlFlag();

    DbgProtPrintSampleInfo();
    c = getchar();
    while( c )
    {
        SYS_TEST("\n");
        switch(c)
        {
            case '0':
                GenerateDebugKey();
                break;
            case '1':
                WriteDebugKeyToOtp();
                break;
            case '2':
                ApplyDebugKeyToReg();
                break;
            case '3':
                ClearDebugKeyInReg();
                break;
            case '4':
                InjectProtect();
                break;
            default:
                SYS_WARN("Cannot find testcase %c!\n", c);
                break;
        }
        DbgProtPrintSampleInfo();
        c = getchar();
    }
}
