/**

    @file       crypto.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/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/IOAddress.h>
#include <asm/nvt-common/rcw_macro.h>
#include <asm/arch/crypto.h>
#include <asm/arch/efuse_protected.h>

#define CRYPTO_SETREG(ofs,value)    	OUTW(IOADDR_CRYPTO_REG_BASE+(ofs),(value))
#define CRYPTO_GETREG(ofs)          	INW(IOADDR_CRYPTO_REG_BASE+(ofs))

#define CRYPTO_REG_BASE_ADDR			IOADDR_CRYPTO_REG_BASE
#define CLOCK_GEN_REG_BASE_ADDR			IOADDR_CG_REG_BASE
//PLL register move to STBC
#define CLOCK_GEN_PLL_PWR_REG			(CLOCK_GEN_REG_BASE_ADDR + 0x00)
#define CLOCK_GEN_PLL_STS_REG			(CLOCK_GEN_REG_BASE_ADDR + 0x04)
#define CLOCK_GEN_ENABLE_REG0           (CLOCK_GEN_REG_BASE_ADDR + 0x70)
#define CKGEN_PLLEN_PLL2                (0x01 << 2)      // PLL2: for Secure boot


#define CRYPTO_DMA_CONFIG_REG_OFS       0x08
#define CRYPTO_DMA_STS_REG_OFS          0x0C

#define CRYPTO_SRC_ADDR_REG_OFS         0x50
#define CRYPTO_DST_ADDR_REG_OFS         0xA4

#define CRYPTO_DMA_CONTROL_REG_OFS      0x04

#define CRYPTO_KEY000_REG_OFS           0x30
#define CRYPTO_KEY032_REG_OFS           0x34
#define CRYPTO_KEY064_REG_OFS           0x38
#define CRYPTO_KEY096_REG_OFS           0x3C

// Crypto
#define CRYPTO_CONFREG_SWRST          	(1<<0)
#define CRYPTO_CONFREG_CRYPTO_EN     	(1<<1)
#define CRYPTO_CONFREG_AES128         	(2<<4)
#define CRYPTO_CONFREG_DECRYPT        	(1<<8)
#define CRYPTO_PIO_DONE               	(1<<0)


#define dma_getPhyAddr(addr)            ((((UINT32)(addr))>=0x80000000UL)?((UINT32)(addr)-0x80000000UL):(UINT32)(addr))


#define SCE_DES_TABLE_NUM        		(40)      //8(header DesTab) + 32(addr DesTab)
#define SCE_DES_WORD_SIZE        		(4)       //1 DesTab = 4 word
#define SCE_DES_IV_OFS           		(8)
#define SCE_DES_CNT_OFS          		(12)
#define SCE_DES_HAEDER_OFS       		(16)
#define SCE_DES_CV_OFS           		(20)
#define SCE_DES_S0_OFS           		(24)
#define SCE_DES_GHASH_OFS        		(28)
#define SCE_DES_BLOCK_CFG_OFS    		(32)

#define SCE_DMA_COUNT                   (1)
#define ROM_AES_SIZE                    (16)

#define _ALIGNED(x) __attribute__((aligned(x)))
static _ALIGNED(32) u32 vuiSCE_DesTab[SCE_DES_TABLE_NUM*SCE_DES_WORD_SIZE];

#define OUTREG32(x, y)      writel(y, x)   //(*((volatile UINT32*)(x)) = (y))    ///< Write 32bits IO register
#define INREG32(x)			readl(x)
#define SETREG32(x, y)      OUTREG32((x), INREG32(x) | (y))     ///< Set 32bits IO register
#define CLRREG32(x, y)      OUTREG32((x), INREG32(x) & ~(y))    ///< Clear 32bits IO register

#ifndef CHKPNT
#define CHKPNT    printf("\033[37mCHK: %d, %s\033[0m\r\n", __LINE__, __func__)
#endif

#ifndef DBGD
#define DBGD(x)   printf("\033[0;35m%s=%d\033[0m\r\n", #x, x)
#endif

#ifndef DBGH
#define DBGH(x)   printf("\033[0;35m%s=0x%08X\033[0m\r\n", #x, x)
#endif

#ifndef DBG_DUMP
#define DBG_DUMP(fmtstr, args...) printf(fmtstr, ##args)
#endif

#ifndef DBG_ERR
#define DBG_ERR(fmtstr, args...)  printf("\033[0;31mERR:%s() \033[0m" fmtstr, __func__, ##args)
#endif

#ifndef DBG_WRN
#define DBG_WRN(fmtstr, args...)  printf("\033[0;33mWRN:%s() \033[0m" fmtstr, __func__, ##args)
#endif

#if 0
#define DBG_IND(fmtstr, args...) printf("%s(): " fmtstr, __func__, ##args)
#else
#ifndef DBG_IND
#define DBG_IND(fmtstr, args...)
#endif
#endif

static u32 crypto_set_mode(u32 uiMode, u32 uiOPMode, u32 uiType)
{
	if (uiMode != CRYPTO_AES) {
		return -1;
	}

	if (uiOPMode >= CRYPTO_OPMODE_NUM) {
		return -1;
	}

	//OUTREG32(CRYPRO_REG_BASE_ADDR, /*(1 << 9) | */(uiMode<<4) | (uiType << 8) | CRYPTO_CONFREG_CRYPTO_EN/* | CRYPTO_CONFREG_SWRST*/); // big endian, descrypt, AES-128, sw-reset
	OUTREG32(CRYPTO_REG_BASE_ADDR, CRYPTO_CONFREG_CRYPTO_EN); // big endian, descrypt, AES-128, sw-reset
	return 0;
}

  //Enable Crypto engine clock    SETREG32(CLOCK_GEN_REG_BASE_ADDR + 0x80, (0x01 << 23));//    uart_putSystemUARTStr("keyoffset=");//    uart_putSystemUARTStr(Dec2HexStr(key_offset));//    uart_putSystemUARTStr("\r\n");    OUTREG32(CRYPRO_REG_BASE_ADDR+0x30, 0x13141516);    OUTREG32(CRYPRO_REG_BASE_ADDR+0x34, 0x09101112);    OUTREG32(CRYPRO_REG_BASE_ADDR+0x38, 0x05060708);    OUTREG32(CRYPRO_REG_BASE_ADDR+0x3C, 0x01020304);    OUTREG32(CRYPRO_REG_BASE_ADDR, (1<<9)|(1<<8)|(2<<1)|(1<<0));    // big endian, descrypt, AES-128, sw-reset//        OUTREG32(CRYPRO_REG_BASE_ADDR, (0<<9)|(1<<8)|(2<<1)|(1<<0));    // little endian, descrypt, AES-128, sw-reset    while (1)       // wait sw reset clear    {        uiReg = INREG32(CRYPRO_REG_BASE_ADDR);        if ((uiReg & (1<<0)) == 0) break;    }


static void crypto_dma_enable(u32 uiMode, u32 uiOPMode, u32 uiType, u32 SrcAddr, u32 DstAddr, u32 Len)
{
	u32 	uiReg;
	u32 	header_config=0;
	u32		block_config =0;
	//clear DMA interrupt status
	uiReg = CRYPTO_GETREG(CRYPTO_DMA_STS_REG_OFS);
	CRYPTO_SETREG(CRYPTO_DMA_STS_REG_OFS, uiReg);

	//Disable interrupt enable
	CRYPTO_SETREG(CRYPTO_DMA_CONFIG_REG_OFS, 0x0);

	memset((void*)&vuiSCE_DesTab[0], 0, SCE_DES_TABLE_NUM*SCE_DES_WORD_SIZE*4);

	//Configure DMA buffer address and size
	header_config = (u32)((1 << 12 ) |((uiOPMode) << 8 ) | (uiMode << 4 ) | uiType);
	//Flush cache

	vuiSCE_DesTab[SCE_DES_HAEDER_OFS] = (u32)header_config;

	block_config = 0;
    vuiSCE_DesTab[SCE_DES_BLOCK_CFG_OFS]    = dma_getPhyAddr(SrcAddr);
    vuiSCE_DesTab[SCE_DES_BLOCK_CFG_OFS+1]  = dma_getPhyAddr(DstAddr);
    vuiSCE_DesTab[SCE_DES_BLOCK_CFG_OFS+2]  = Len;

    block_config = (u32)(1);

    vuiSCE_DesTab[SCE_DES_BLOCK_CFG_OFS+3]  = block_config;
   	flush_dcache_range((ulong)&vuiSCE_DesTab[0], (ulong)&vuiSCE_DesTab[0] + roundup(SCE_DES_TABLE_NUM *SCE_DES_WORD_SIZE*4, ARCH_DMA_MINALIGN));
   	flush_dcache_range(SrcAddr, SrcAddr + roundup(Len, ARCH_DMA_MINALIGN));
	invalidate_dcache_range(DstAddr, DstAddr + roundup(Len, ARCH_DMA_MINALIGN));

	CRYPTO_SETREG(CRYPTO_SRC_ADDR_REG_OFS, ((ulong)&vuiSCE_DesTab[0]));
	//Set DMA Enable
	CRYPTO_SETREG(CRYPTO_DMA_CONTROL_REG_OFS, (0x1<<4));
	while (!(CRYPTO_GETREG(CRYPTO_DMA_STS_REG_OFS) & (0x1<<4)));
	CRYPTO_SETREG(CRYPTO_DMA_STS_REG_OFS, (0x1<<4));
}

s32 crypto_data_operation(EFUSE_OTP_KEY_SET_FIELD key_set, CRYPT_OP crypt_op_param)
{

	UINT32  reg;
	s32  	ret;

	reg = INREG32(CLOCK_GEN_PLL_STS_REG);

	printf("bb\r\n");
	if((reg & CKGEN_PLLEN_PLL2) != CKGEN_PLLEN_PLL2) {

		SETREG32(CLOCK_GEN_PLL_PWR_REG, CKGEN_PLLEN_PLL2);

		while (1) // wait PLL2 enabled
	    {
	        if (INREG32(CLOCK_GEN_PLL_STS_REG) & (CKGEN_PLLEN_PLL2))
	        {
	            break;
	        }
	    }

	}
	SETREG32(CLOCK_GEN_ENABLE_REG0, (0x01 << 31));

	ret = crypto_set_mode(CRYPTO_AES, crypt_op_param.op_mode, crypt_op_param.en_de_crypt);
	if (ret < 0) {
		return ret;
	}
	otp_set_key_destination(key_set);
	crypto_dma_enable(CRYPTO_AES, crypt_op_param.op_mode, crypt_op_param.en_de_crypt, crypt_op_param.src_addr, crypt_op_param.dst_addr, crypt_op_param.length);
	CLRREG32(CLOCK_GEN_ENABLE_REG0, (0x01 << 31));
	return 0;
}



//CRYPTO_AES, crypt_op_param.op_mode, crypt_op_param.en_de_crypt, CRYPTO_DMA);

static void crypto_setKey(u8 *ucKey)
{
	u8  *u32_key = ucKey;

	CRYPTO_SETREG(CRYPTO_KEY000_REG_OFS, *(u32 *)(u32_key +  0));
	CRYPTO_SETREG(CRYPTO_KEY032_REG_OFS, *(u32 *)(u32_key +  4));
	CRYPTO_SETREG(CRYPTO_KEY064_REG_OFS, *(u32 *)(u32_key +  8));
	CRYPTO_SETREG(CRYPTO_KEY096_REG_OFS, *(u32 *)(u32_key + 12));
}

s32 crypto_data_operation_by_key(UINT8 * key, CRYPT_OP crypt_op_param)
{
	s32  	ret;
	UINT32  reg;

	reg = INREG32(CLOCK_GEN_REG_BASE_ADDR + 0x4);

	if((reg & CKGEN_PLLEN_PLL2) != CKGEN_PLLEN_PLL2) {

		SETREG32(CLOCK_GEN_PLL_PWR_REG, CKGEN_PLLEN_PLL2);

		while (1) // wait PLL2 enabled
	    {
	        if (INREG32(CLOCK_GEN_PLL_STS_REG) & (CKGEN_PLLEN_PLL2))
	        {
	            break;
	        }
	    }
	}
	SETREG32(CLOCK_GEN_ENABLE_REG0, (0x01 << 31));

	ret = crypto_set_mode(CRYPTO_AES, crypt_op_param.op_mode, crypt_op_param.en_de_crypt);
	if (ret < 0) {
		return ret;
	}
	crypto_setKey(key);

	crypto_dma_enable(CRYPTO_AES, crypt_op_param.op_mode, crypt_op_param.en_de_crypt, crypt_op_param.src_addr, crypt_op_param.dst_addr, crypt_op_param.length);

	CLRREG32(CLOCK_GEN_ENABLE_REG0, (0x01 << 31));

	return 0;
}

