/*
 * (C) Copyright 2013-2016
* SPDX-License-Identifier:	GPL-2.0+
 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
 *
 */
/*
*********************************************************************************************************
*											        eBIOS
*						                the Base Input Output Subrutines
*									           dma controller sub system
*
*						        (c) Copyright 2006-2007, RICHARD,China
*											All	Rights Reserved
*
* File    : nfc_for_boot0.c
* By      : Neil Peng.x
* Version : V1.00
* note:   1. always select chip0, rb0
*         2. only need read single page, read id, reset....
*
*********************************************************************************************************
*/
#ifndef _NFC_BOOT0_H_
#define _NFC_BOOT0_H_

#include "../include/nand_drv_cfg.h"
#include "../include/nfc.h"

__u32 NandIOBase[2];
__u32 NandIndex;
__u32 pagesize;

__u8 read_retry_reg_adr[READ_RETRY_MAX_REG_NUM]			   = { 0 };
__u8 read_retry_default_val[1][READ_RETRY_MAX_REG_NUM]		   = { { 0 } };
__s16 read_retry_val[READ_RETRY_MAX_CYCLE][READ_RETRY_MAX_REG_NUM] = { { 0 } };
__u8 hynix_read_retry_otp_value[1][8][8]     = { { { 0 } } };
__u8 read_retry_mode;
__u8 read_retry_cycle;
__u8 read_retry_reg_num;
__u8 hynix16nm_read_retry_otp_value[1][8][4] = { { { 0 } } };

const __s16 para0[6][4] = { { 0x00, 0x06, 0x0A, 0x06 },
			    { 0x00, -0x03, -0x07, -0x08 },
			    { 0x00, -0x06, -0x0D, -0x0F },
			    { 0x00, -0x0B, -0x14, -0x17 },
			    { 0x00, 0x00, -0x1A, -0x1E },
			    { 0x00, 0x00, -0x20, -0x25 } };
const __s16 para1[6][4] = { { 0x00, 0x06, 0x0a, 0x06 },
			    { 0x00, -0x03, -0x07, -0x08 },
			    { 0x00, -0x06, -0x0d, -0x0f },
			    { 0x00, -0x09, -0x14, -0x17 },
			    { 0x00, 0x00, -0x1a, -0x1e },
			    { 0x00, 0x00, -0x20, -0x25 } };
const __s16 param0x40[10] = { 0x0, 0x0,  0x0,  0x1,  0x2,
			      0x3, 0x04, 0x05, 0x06, 0x07 };
const __s16 param0x41[12] = { 0x0,  0x0,  0x0,  0x1,  0x2,  0x3,
			      0x04, 0x05, 0x06, 0x07, 0x08, 0x0C };

__u32 ddr_param[8];

void NFC_InitDDRParam(__u32 chip, __u32 param)
{
	if (chip < 8)
		ddr_param[chip] = param;
}
void nfc_repeat_mode_enable(void)
{
	__u32 reg_val;
	reg_val = NFC_READ_REG(NFC_REG_CTL);
	if (((reg_val >> 18) & 0x3) > 1) {
		/*ddr type*/
		reg_val |= 0x1 << 20;
		NFC_WRITE_REG(NFC_REG_CTL, reg_val);
	}
}

void nfc_repeat_mode_disable(void)
{
	__u32 reg_val;
	reg_val = NFC_READ_REG(NFC_REG_CTL);
	if (((reg_val >> 18) & 0x3) > 1) {
		/*ddr type*/
		reg_val &= (~(0x1 << 20));
		NFC_WRITE_REG(NFC_REG_CTL, reg_val);
	}
}

/*******************wait nfc********************************************/
__s32 _wait_cmdfifo_free(void)
{
	__s32 timeout = 0xffff;
	while ((timeout--) && (NFC_READ_REG(NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
		;
	if (timeout <= 0)
		return -ERR_TIMEOUT;
	return 0;
}

__s32 _wait_cmd_finish(void)
{
	__s32 timeout = 0xffff;
	while ((timeout--) && !(NFC_READ_REG(NFC_REG_ST) & NFC_CMD_INT_FLAG))
		;
	if (timeout <= 0)
		return -ERR_TIMEOUT;
	NFC_WRITE_REG(NFC_REG_ST, NFC_READ_REG(NFC_REG_ST) & NFC_CMD_INT_FLAG);
	return 0;
}

void _dma_config_start(__u8 rw, __u32 buff_addr, __u32 len)
{
	__u32 reg_val;

	if (buff_addr & 0x3) {
		PRINT("[!!!!]%s(): buff addr(0x%x) is not 32bit aligned, "
		      "and it will be clipped to 0x%x",
		      "_dma_config_start", buff_addr, (buff_addr & 0x3));
	}
	reg_val = NFC_READ_REG(NFC_REG_CTL);
	reg_val &= (~(0x1 << 15));
	reg_val |= 0x1U << 14;
	NFC_WRITE_REG(NFC_REG_CTL, reg_val);

	ndfc_dma_desc[0].bcnt = 0;
	ndfc_dma_desc[0].bcnt |= NDFC_DESC_BSIZE(len);
	ndfc_dma_desc[0].buff = buff_addr;

	ndfc_dma_desc[0].cfg = 0;
	ndfc_dma_desc[0].cfg |= NDFC_DESC_FIRST_FLAG;
	ndfc_dma_desc[0].cfg |= NDFC_DESC_LAST_FLAG;
	ndfc_dma_desc[0].next = (struct _nfc_dma_desc_t *)ndfc_dma_desc;

	NFC_WRITE_REG(NFC_REG_DMA_DL_BASE, (ulong)ndfc_dma_desc);
}

__s32 _wait_dma_end(void)
{
	__s32 timeout = 0xffff;

	while ((timeout--) && (!(NFC_READ_REG(NFC_REG_ST) & NFC_DMA_INT_FLAG)))
		;
	if (timeout <= 0)
		return -ERR_TIMEOUT;

	return 0;
}

__s32 _reset(void)
{
	__u32 cfg;
	__s32 timeout = 0xffff;

	/*reset NFC*/
	cfg = NFC_READ_REG(NFC_REG_CTL);
	cfg |= NFC_RESET;
	NFC_WRITE_REG(NFC_REG_CTL, cfg);
	/*waiting reset operation end*/
	while ((timeout--) && (NFC_READ_REG(NFC_REG_CTL) & NFC_RESET))
		;
	if (timeout <= 0)
		return -ERR_TIMEOUT;

	return 0;
}

/***************ecc function*****************************************/
__s32 _check_ecc(__u32 eblock_cnt)
{
	__u32 i;
	__u32 ecc_mode;
	__u32 max_ecc_bit_cnt = 16;
	__u32 cfg;
	__u32 ecc_cnt_w[8]; /*ecc_cnt_w[4];*/
	__u8 *ecc_cnt;
	__u8 ecc_tab[10] = { 16, 24, 28, 32, 40, 48, 56, 60, 64, 72 };

	ecc_mode	= (NFC_READ_REG(NFC_REG_ECC_CTL) >> 12) & 0xf;
	max_ecc_bit_cnt = ecc_tab[ecc_mode];

	/*check ecc errro*/
	/*cfg = NFC_READ_REG(NFC_REG_ECC_ST)&0xffff;*/
	cfg = NFC_READ_REG(NFC_REG_ERR_ST);
	for (i = 0; i < eblock_cnt; i++) {
		if (cfg & (1 << i))
			return -ERR_ECC;
	}

	/*check ecc limit*/
	ecc_cnt_w[0] = NFC_READ_REG(NFC_REG_ECC_CNT0);
	ecc_cnt_w[1] = NFC_READ_REG(NFC_REG_ECC_CNT1);
	ecc_cnt_w[2] = NFC_READ_REG(NFC_REG_ECC_CNT2);
	ecc_cnt_w[3] = NFC_READ_REG(NFC_REG_ECC_CNT3);
	ecc_cnt_w[4] = NFC_READ_REG(NFC_REG_ECC_CNT4);
	ecc_cnt_w[5] = NFC_READ_REG(NFC_REG_ECC_CNT5);
	ecc_cnt_w[6] = NFC_READ_REG(NFC_REG_ECC_CNT6);
	ecc_cnt_w[7] = NFC_READ_REG(NFC_REG_ECC_CNT7);

	ecc_cnt = (__u8 *)((ulong)(ecc_cnt_w));
	for (i = 0; i < eblock_cnt; i++) {
		if ((max_ecc_bit_cnt - 4) <= ecc_cnt[i])
			return ECC_LIMIT;
	}

	return 0;
}

void _disable_ecc(void)
{
	__u32 cfg = NFC_READ_REG(NFC_REG_ECC_CTL);
	cfg &= ((~NFC_ECC_EN) & 0xffffffff);
	NFC_WRITE_REG(NFC_REG_ECC_CTL, cfg);
}

void _enable_ecc(__u32 pipline)
{
	__u32 cfg = NFC_READ_REG(NFC_REG_ECC_CTL);
	if (pipline == 1)
		cfg |= NFC_ECC_PIPELINE;
	else
		cfg &= ((~NFC_ECC_PIPELINE) & 0xffffffff);

	/*after erased, all data is 0xff, but ecc is not 0xff,
			so ecc asume it is right*/
	/*if random open, disable exception*/
	if (cfg & (0x1 << 5))
		cfg &= (~(0x1 << 4));
	else
		cfg |= (1 << 4);

	cfg |= NFC_ECC_EN;
	NFC_WRITE_REG(NFC_REG_ECC_CTL, cfg);
}

void _set_addr(__u8 *addr, __u8 cnt)
{
	__u32 i;
	__u32 addr_low  = 0;
	__u32 addr_high = 0;

	for (i = 0; i < cnt; i++) {
		if (i < 4)
			addr_low |= (addr[i] << (i * 8));
		else
			addr_high |= (addr[i] << ((i - 4) * 8));
	}

	NFC_WRITE_REG(NFC_REG_ADDR_LOW, addr_low);
	NFC_WRITE_REG(NFC_REG_ADDR_HIGH, addr_high);
}

/*user data length settin function*/

/*****************************************************************************
*Name         :
*Description  :
*Parameter    :
*Return       : 0:ok  -1:fail
*Note         : udata_cnt is 4 bytes aligned
*****************************************************************************/
#define MAX_UDATA_LEN_FOR_ECCBLOCK (32)
#define MAX_ECC_BLK_CNT (32)
#define UDATA_LEN_FOR_4BYTESPER1K (4)
s32 ndfc_set_user_data_len(u32 udata_cnt)
{
#if 1

	u32 udata_len[10] = { 0, 4, 8, 12, 16, 20, 24, 28, 32 };
	u32 i, ecc_block_cnt, last_udata_len;
	u8 last_udata_len_mode = 0;
	u8 udata_len_mode[32]  = {
		0,
	}; /*user data lenth for every ecc block,udata_len is 4 bytes aligned*/
	u32 cfg       = 0;
	ecc_block_cnt = (udata_cnt + MAX_UDATA_LEN_FOR_ECCBLOCK) /
			MAX_UDATA_LEN_FOR_ECCBLOCK;
	last_udata_len = udata_cnt % MAX_UDATA_LEN_FOR_ECCBLOCK;

	for (i = 0; i < 10; i++) {
		if (last_udata_len == udata_len[i]) {
			last_udata_len_mode = i;
			break;
		}
	}

	for (i = 0; i < MAX_ECC_BLK_CNT; i++) {
		if (i < (ecc_block_cnt - 1))
			udata_len_mode[i] = 8;
		else if (i == (ecc_block_cnt - 1))
			udata_len_mode[i] = last_udata_len_mode;
		else
			udata_len_mode[i] = 0;

		cfg |= udata_len_mode[i]
		       << ((i & 0x07)
			   << 2); /*4 bit indecate one ecc block mode. a 32 register include 8 ecc block mode.*/
		if (((i + 1) & 0x07) == 0) {
			NFC_WRITE_REG(NFC_REG_USER_DATA_LEN((i >> 3)), cfg);
			cfg = 0;
		}
	}
#else /* udata_len for every ecc block is 4 bytes*/
	u32 i = 0;
	u32 ecc_block_cnt;
	u8 udata_len_mode[32] = {
		0,
	}; /*user data lenth for every ecc block,udata_len is 4 bytes aligned*/
	u32 cfg = 0;

	ecc_block_cnt = (udata_cnt + UDATA_LEN_FOR_4BYTESPER1K - 1) /
			UDATA_LEN_FOR_4BYTESPER1K;

	for (i = 0; i < MAX_ECC_BLK_CNT; i++) {
		if (i < ecc_block_cnt) {
			udata_len_mode[i] = 1;
		} else {
			udata_len_mode[i] = 0;
		}

		cfg |= udata_len_mode[i]
		       << ((i & 0x07)
			   << 2); /*4 bit indecate one ecc block mode. a 32 register include 8 ecc block mode.*/
		if (((i + 1) & 0x07) == 0) {
			NFC_WRITE_REG(NFC_REG_USER_DATA_LEN((i >> 3)), cfg);
			cfg = 0;
		}
	}

#endif
	return 0;
}

__s32 _read_in_page_mode(NFC_CMD_LIST *rcmd, void *mainbuf, void *sparebuf,
			 __u8 read_mode)
{
	__s32 ret, ret1;
	__s32 i;
	__u32 cfg;
	NFC_CMD_LIST *cur_cmd, *read_addr_cmd;
	__u32 read_data_cmd, random_read_cmd0, random_read_cmd1;
	__u32 page_size_temp = 0, ecc_mode_temp = 0, pagesizebytecnt = 0;
	__u32 blk_cnt, blk_mask;

	ret		 = 0;
	read_addr_cmd    = rcmd;
	cur_cmd		 = rcmd;
	cur_cmd		 = cur_cmd->next;
	random_read_cmd0 = cur_cmd->value;
	cur_cmd		 = cur_cmd->next;
	random_read_cmd1 = cur_cmd->value;
	cur_cmd		 = cur_cmd->next;
	read_data_cmd    = cur_cmd->value;

	/*access NFC internal RAM by DMA bus*/
	NFC_WRITE_REG(NFC_REG_CTL,
		      (NFC_READ_REG(NFC_REG_CTL)) | NFC_RAM_METHOD);

	/*set pagesize to 1K*/
	if (read_mode == 1) {
		page_size_temp = (NFC_READ_REG(NFC_REG_CTL) & 0xf00) >> 8;
		NFC_WRITE_REG(NFC_REG_CTL,
			      (NFC_READ_REG(NFC_REG_CTL)) | (0x3 << 8));
		pagesizebytecnt = 1024;
	} else {
		pagesizebytecnt = pagesize;
	}

	/*setting user data len  and user data*/
	/* 4byte for 1K ecc block data,the max user data length is 16*/
	ndfc_set_user_data_len(((pagesize >> 8) < 16) ? (pagesize >> 8) : 16);

	_dma_config_start(0, (ulong)mainbuf, pagesizebytecnt);

	/*wait cmd fifo free*/
	ret = _wait_cmdfifo_free();
	if (ret)
		return ret;

	/*set NFC_REG_CNT*/
	NFC_WRITE_REG(NFC_REG_CNT, 1024);

	/*set NFC_REG_RCMD_SET*/
	cfg = 0;
	cfg |= (read_data_cmd & 0xff);
	cfg |= ((random_read_cmd0 & 0xff) << 8);
	cfg |= ((random_read_cmd1 & 0xff) << 16);
	NFC_WRITE_REG(NFC_REG_RCMD_SET, cfg);

	/*set NFC_REG_BLOCK_MASK*/
	blk_cnt  = pagesize / 1024;
	blk_mask = ((1 << (blk_cnt - 1)) | ((1 << (blk_cnt - 1)) - 1));
	NFC_WRITE_REG(NFC_REG_BLOCK_MASK, blk_mask);

	/*set addr*/
	_set_addr(read_addr_cmd->addr, read_addr_cmd->addr_cycle);

	/*set NFC_REG_CMD*/
	cfg = 0;
	cfg |= read_addr_cmd->value;
	/*set sequence mode*/
	if (read_mode == 1)
		cfg |= 0x1 << 25;

	cfg |= ((read_addr_cmd->addr_cycle - 1) << 16);
	cfg |= (NFC_SEND_ADR | NFC_DATA_TRANS | NFC_SEND_CMD1 | NFC_SEND_CMD2 |
		NFC_WAIT_FLAG | NFC_DATA_SWAP_METHOD);
	cfg |= ((__u32)0x2 << 30); /*page command*/

	/*enable ecc*/
	_enable_ecc(1);

	/*set ecc to 64-bit ecc*/
	if (read_mode == 1) {
		ecc_mode_temp = NFC_READ_REG(NFC_REG_ECC_CTL) & 0xff00;
		NFC_WRITE_REG(NFC_REG_ECC_CTL, ((NFC_READ_REG(NFC_REG_ECC_CTL) &
						 (~NFC_ECC_MODE)) |
						(0x0A << 8)));
	}
#ifdef FPGA_PLATFORM
	asm volatile("dsb");
	asm volatile("isb");
#endif
	NFC_WRITE_REG(NFC_REG_CMD, cfg);

	/*while(*((volatile int *)0x01c03900)!=0x12345678);*/

	NAND_WaitDmaFinish(); /**/

	/*wait cmd fifo free and cmd finish*/
	ret = _wait_cmdfifo_free();
	ret |= _wait_cmd_finish();
	if (ret) {
		_disable_ecc();
		return ret;
	}
	/*get user data*/
	for (i = 0; i < pagesizebytecnt / 1024; i++) {
		*(((__u32 *)sparebuf) + i) = NFC_READ_REG(NFC_REG_USER_DATA(i));
	}

	/*ecc check and disable ecc*/
	ret = _check_ecc(pagesizebytecnt / 1024);
	_disable_ecc();

	/*if dma mode is wait*/
	ret1 = _wait_dma_end();
	if (ret1)
		return ret1;

	if (read_mode == 1) {
		/*set ecc to original value*/
		NFC_WRITE_REG(NFC_REG_ECC_CTL,
			      (NFC_READ_REG(NFC_REG_ECC_CTL) &
			       (~NFC_ECC_MODE)) |
				      ecc_mode_temp);
		/*set pagesize to original value*/
		NFC_WRITE_REG(NFC_REG_CTL,
			      ((NFC_READ_REG(NFC_REG_CTL)) & (~NFC_PAGE_SIZE)) |
				      (page_size_temp << 8));
	}

	return ret;
}

/*******************************************************************************
*								NFC_Read
*
* Description 	: read some sectors data from flash in single plane mode.
* Arguments	: *rcmd	-- the read command sequence list head。
*			  *mainbuf	-- point to data buffer address, 	it must be four bytes align.
*                     *sparebuf	-- point to spare buffer address.
*                     dma_wait_mode	-- how to deal when dma start, 0 = wait till dma finish,
							    1 = dma interrupt was set and now sleep till interrupt occurs.
*			  page_mode  -- 0 = normal command, 1 = page mode
* Returns		: 0 = success.
			  1 = success & ecc limit.
			  -1 = too much ecc err.
* Notes		:  if align page data required，page command mode is used., if the commands do
			   not fetch data，ecc is not neccesary.
********************************************************************************/
__s32 NFC_Read(NFC_CMD_LIST *rcmd, void *mainbuf, void *sparebuf,
	       __u8 read_mode, __u8 page_mode)
{
	__s32 ret;
	ret = _read_in_page_mode(rcmd, mainbuf, sparebuf, read_mode);
	/*switch to ahb*/
	NFC_WRITE_REG(NFC_REG_CTL,
		      (NFC_READ_REG(NFC_REG_CTL)) & (~NFC_RAM_METHOD));
	return ret;
}

/*finish the comand list */
__s32 nfc_set_cmd_register(NFC_CMD_LIST *cmd)
{
	__u32 cfg;
	__s32 ret;

	NFC_CMD_LIST *cur_cmd = cmd;
	while (cur_cmd != NULL) {
		/*wait cmd fifo free*/
		ret = _wait_cmdfifo_free();
		if (ret)
			return ret;

		cfg = 0;
		/*set addr*/
		if (cur_cmd->addr_cycle) {
			_set_addr(cur_cmd->addr, cur_cmd->addr_cycle);
			cfg |= ((cur_cmd->addr_cycle - 1) << 16);
			cfg |= NFC_SEND_ADR;
		}

		/*set NFC_REG_CMD*/
		/*set cmd value*/
		cfg |= cur_cmd->value;
		/*set sequence mode*/
		/*cfg |= 0x1<<25;*/
		/*wait rb?*/
		if (cur_cmd->wait_rb_flag) {
			cfg |= NFC_WAIT_FLAG;
		}
		if (cur_cmd->data_fetch_flag) {
			NFC_WRITE_REG(NFC_REG_CTL,
				      (NFC_READ_REG(NFC_REG_CTL)) &
					      (~NFC_RAM_METHOD));
			cfg |= NFC_DATA_TRANS;
			NFC_WRITE_REG(NFC_REG_CNT, cur_cmd->bytecnt);
		}
		/*send command*/
		cfg |= NFC_SEND_CMD1;
		NFC_WRITE_REG(NFC_REG_CMD, cfg);
		cur_cmd = cur_cmd->next;
	}
	return 0;
}

__s32 NFC_SetRandomSeed(__u32 random_seed)
{
	__u32 cfg;
	cfg = NFC_READ_REG(NFC_REG_ECC_CTL);
	cfg &= 0x0000ffff;
	cfg |= (random_seed << 16);
	NFC_WRITE_REG(NFC_REG_ECC_CTL, cfg);
	return 0;
}

__s32 NFC_RandomEnable(void)
{
	__u32 cfg;
	cfg = NFC_READ_REG(NFC_REG_ECC_CTL);
	cfg |= (0x1 << 5);
	NFC_WRITE_REG(NFC_REG_ECC_CTL, cfg);
	return 0;
}

__s32 NFC_RandomDisable(void)
{
	__u32 cfg;
	cfg = NFC_READ_REG(NFC_REG_ECC_CTL);
	cfg &= (~(0x1 << 5));
	NFC_WRITE_REG(NFC_REG_ECC_CTL, cfg);

	return 0;
}

/*******************************************************************************
*								NFC_GetId
*
* Description 	: get chip id.
* Arguments	: *idcmd	-- the get id command sequence list head.

* Returns		: 0 = success.
			  -1 = fail.
* Notes		:
********************************************************************************/
__s32 NFC_GetId(NFC_CMD_LIST *idcmd, __u8 *idbuf)
{
	__u32 i;
	__s32 ret;

	nfc_repeat_mode_enable();
	ret = nfc_set_cmd_register(idcmd);
	if (ret) {
		return ret;
	}

	ret = _wait_cmdfifo_free();
	ret |= _wait_cmd_finish();

	/*get 6 bytes id value*/
	for (i = 0; i < 6; i++) {
		*(idbuf + i) = NFC_READ_RAM_B(NFC_RAM0_BASE + i);
	}

	nfc_repeat_mode_disable();
	return ret;
}

/*******************************************************************************
*								NFC_GetId
*
* Description 	: get chip id.
* Arguments	: *idcmd	-- the get id command sequence list head.

* Returns		: 0 = success.
			  -1 = fail.
* Notes		:
********************************************************************************/
__s32 NFC_GetFeature(NFC_CMD_LIST *cmd, __u8 *buf)
{
	__u32 i;
	__s32 ret;

	nfc_repeat_mode_enable();
	ret = nfc_set_cmd_register(cmd);
	if (ret) {
		return ret;
	}

	ret = _wait_cmdfifo_free();
	ret |= _wait_cmd_finish();

	/*get 4 bytes feature value*/
	for (i = 0; i < 4; i++) {
		*(buf + i) = NFC_READ_RAM_B(NFC_RAM0_BASE + i);
	}

	nfc_repeat_mode_disable();

	return ret;
}

/*******************************************************************************
*								NFC_GetId
*
* Description 	: get chip id.
* Arguments	: *idcmd	-- the get id command sequence list head.

* Returns		: 0 = success.
			  -1 = fail.
* Notes		:
********************************************************************************/
__s32 NFC_SetFeature(NFC_CMD_LIST *cmd, __u8 *buf)
{
	__s32 ret;
	__u32 cfg;

	nfc_repeat_mode_enable();

	/* access NFC internal RAM by AHB bus */
	NFC_WRITE_REG(NFC_REG_CTL,
		      (NFC_READ_REG(NFC_REG_CTL)) & (~NFC_RAM_METHOD));

	/* set data */
	NFC_WRITE_RAM_B(NFC_RAM0_BASE + 0, buf[0]);
	NFC_WRITE_RAM_B(NFC_RAM0_BASE + 1, buf[1]);
	NFC_WRITE_RAM_B(NFC_RAM0_BASE + 2, buf[2]);
	NFC_WRITE_RAM_B(NFC_RAM0_BASE + 3, buf[3]);
	NFC_WRITE_REG(NFC_REG_CNT, 4);

	/*wait cmd fifo free*/
	ret = _wait_cmdfifo_free();
	if (ret)
		return ret;

	cfg = 0;
	/*set addr*/
	if (cmd->addr_cycle) {
		_set_addr(cmd->addr, cmd->addr_cycle);
		cfg |= ((cmd->addr_cycle - 1) << 16);
		cfg |= NFC_SEND_ADR;
	}

	cfg |= NFC_ACCESS_DIR | NFC_WAIT_FLAG | NFC_DATA_TRANS | NFC_SEND_CMD1;
	cfg |= (cmd->value & 0xff);

	/*set command io */
	NFC_WRITE_REG(NFC_REG_CMD, cfg);

	ret = _wait_cmdfifo_free();
	ret |= _wait_cmd_finish();
	if (ret) {
		return ret;
	}

	nfc_repeat_mode_disable();
	return ret;
}

/*****************************************************************************
*Name         :
*Description  :
*Parameter    :
*Return       : 0:ok  -1:fail
*Note         :
*****************************************************************************/
__s32 NFC_Is_Toggle_Interface(void)
{
	u32 cfg = 0;
	cfg     = NFC_READ_REG(NFC_REG_CTL);
	if ((cfg >> 18) & 0x3) {
		return 1;
	}
	return 0;
}

/*****************************************************************************
*Name         :
*Description  :
*Parameter    :
*Return       : 0:ok  -1:fail
*Note         :
*****************************************************************************/
__s32 NFC_Set_Legacy_Interface(void)
{
	u32 cfg = 0;
	cfg     = NFC_READ_REG(NFC_REG_CTL);

	cfg &= ~(0x3 << 18);
	NFC_WRITE_REG(NFC_REG_CTL, cfg);

	return 0;
}

/*****************************************************************************
*Name         :
*Description  :
*Parameter    :
*Return       : 0:ok  -1:fail
*Note         :
*****************************************************************************/
__s32 NFC_Set_Toggle_Interface(void)
{
	u32 cfg = 0;
	cfg     = NFC_READ_REG(NFC_REG_CTL);
	cfg |= 0x3 << 18;
	NFC_WRITE_REG(NFC_REG_CTL, cfg);

	return 0;
}
/*******************************************************************************
*								NFC_GetStatus
*
* Description 	: get status.
* Arguments	: *scmd	-- the get status command sequence list head.

* Returns		: status result
* Notes		: some cmd must be sent with addr.
********************************************************************************/
__s32 NFC_GetStatus(NFC_CMD_LIST *scmd)
{
	__s32 ret;

	nfc_repeat_mode_enable();
	ret = nfc_set_cmd_register(scmd);
	if (ret) {
		return ret;
	}

	ret = _wait_cmdfifo_free();
	ret |= _wait_cmd_finish();
	if (ret) {
		return ret;
	}

	nfc_repeat_mode_disable();
	return NFC_READ_RAM_B(NFC_RAM0_BASE);
}
/*******************************************************************************
*								NFC_ResetChip
*
* Description 	: reset nand flash.
* Arguments	: *reset_cmd	-- the reset command sequence list head.

* Returns		: sucess or fail
* Notes		:
********************************************************************************/
__s32 NFC_ResetChip(NFC_CMD_LIST *reset_cmd)

{
	__s32 ret;

	ret = nfc_set_cmd_register(reset_cmd);
	if (ret) {
		return ret;
	}
	ret = _wait_cmdfifo_free();
	ret |= _wait_cmd_finish();

	return ret;
}

/*******************************************************************************
*								NFC_SelectChip
*
* Description 	: enable chip ce.
* Arguments	: chip	-- chip no.

* Returns		: 0 = sucess -1 = fail
* Notes		:
********************************************************************************/
__s32 NFC_SelectChip(__u32 chip)
{
	__u32 cfg;

	cfg = NFC_READ_REG(NFC_REG_CTL);
	cfg &= ((~NFC_CE_SEL) & 0xffffffff);
	cfg |= ((chip & 0x7) << 24);
	NFC_WRITE_REG(NFC_REG_CTL, cfg);

	return 0;
}

/*******************************************************************************
*								NFC_SelectRb
*
* Description 	: select rb.
* Arguments	: rb	-- rb no.

* Returns		: 0 = sucess -1 = fail
* Notes		:
********************************************************************************/
__s32 NFC_SelectRb(__u32 rb)
{
	__s32 cfg;

	cfg = NFC_READ_REG(NFC_REG_CTL);
	cfg &= ((~NFC_RB_SEL) & 0xffffffff);
	cfg |= ((rb & 0x1) << 3);
	NFC_WRITE_REG(NFC_REG_CTL, cfg);

	return 0;
}

__s32 NFC_DeSelectChip(__u32 chip)
{
	return 0;
}

__s32 NFC_DeSelectRb(__u32 rb)
{
	return 0;
}

/*******************************************************************************
*								NFC_CheckRbReady
*
* Description 	: check rb if ready.
* Arguments	: rb	-- rb no.

* Returns		: 0 = sucess -1 = fail
* Notes		:
********************************************************************************/

__s32 NFC_CheckRbReady(__u32 rb)
{
	__s32 ret;
	__u32 cfg = NFC_READ_REG(NFC_REG_ST);

	cfg &= (NFC_RB_STATE0 << (rb & 0x3));

	if (cfg)
		ret = 0;
	else
		ret = -1;

	return ret;
}

/*******************************************************************************
*								NFC_ChangeMode
*
* Description 	: change serial access mode when clock change.
* Arguments	: nand_info -- structure with flash bus width,pagesize ,serial access mode and other configure parametre

* Returns		: 0 = sucess -1 = fail
* Notes		: NFC must be reset before seial access mode changes.
********************************************************************************/
__s32 NFC_ChangMode(NFC_INIT_INFO *nand_info)
{
	__u32 cfg;

	pagesize = nand_info->pagesize * 512;

	/*reset nfc*/
	_reset();

	/*set NFC_REG_CTL*/
	cfg = 0;
	cfg |= NFC_EN;
	cfg |= ((nand_info->bus_width & 0x1) << 2);
	cfg |= ((nand_info->ce_ctl & 0x1) << 6);
	cfg |= ((nand_info->ce_ctl1 & 0x1) << 7);
	if (nand_info->pagesize == 2) /*  1K  */
		cfg |= (0x0 << 8);
	else if (nand_info->pagesize == 4) /*  2K  */
		cfg |= (0x1 << 8);
	else if (nand_info->pagesize == 8) /*  4K  */
		cfg |= (0x2 << 8);
	else if (nand_info->pagesize == 16) /*  8K  */
		cfg |= (0x3 << 8);
	else if ((nand_info->pagesize > 16) &&
		 (nand_info->pagesize < 32)) /*  12K  */
		cfg |= (0x4 << 8);
	else if (nand_info->pagesize == 32) /*  16K  */
		cfg |= (0x4 << 8);
	else /* default 4K */
		cfg |= (0x2 << 8);
	cfg |= ((nand_info->ddr_type & 0x3) << 18); /*set ddr type*/
	cfg |= ((nand_info->debug & 0x1) << 31);
	NFC_WRITE_REG(NFC_REG_CTL, cfg);

	/*set NFC_TIMING */
	cfg = 0;
	if ((nand_info->ddr_type & 0x3) == 0) {
/*
	 * There is a timing issue on V7 FPGA_PLATFORM.
	 * We need set serial_access_mode 0(normal mode) on V7 FPGA_PLATFORM.
	 */
#ifndef FPGA_PLATFORM
		cfg |= ((nand_info->serial_access_mode & 0x1) & 0xf) << 8;
#endif
	} else if ((nand_info->ddr_type & 0x3) == 2) {
		cfg |= 0x3f;
		cfg |= 0x3 << 8;
	} else if ((nand_info->ddr_type & 0x3) == 3) {
		cfg |= 0x1f;
		cfg |= 0x2 << 8;
	}
	NFC_WRITE_REG(NFC_REG_TIMING_CTL, cfg);
	NFC_WRITE_REG(NFC_REG_TIMING_CFG, 0xff);
	/*set NFC_SPARE_AREA */
	NFC_WRITE_REG(NFC_REG_SPARE_AREA, pagesize);

	return 0;
}

__s32 NFC_SetEccMode(__u8 ecc_mode)
{
	__u32 cfg = NFC_READ_REG(NFC_REG_ECC_CTL);

	cfg &= ((~NFC_ECC_MODE) & 0xffffffff);
	cfg |= (NFC_ECC_MODE & (ecc_mode << 8));
	NFC_WRITE_REG(NFC_REG_ECC_CTL, cfg);

	return 0;
}
/*******************************************************************************
*								NFC_Init
*
* Description 	: init hardware, set NFC, set TIMING, request dma .
* Arguments	: nand_info -- structure with flash bus width,pagesize ,serial access mode and other configure parametre

* Returns		: 0 = sucess -1 = fail
* Notes		: .
********************************************************************************/
__s32 NFC_Init(NFC_INIT_INFO *nand_info)
{
	__s32 ret;
	__s32 i;

	/*init ddr_param*/
	for (i		     = 0; i < 8; i++)
		ddr_param[i] = 0;

	NandIOBase[0] = (ulong)NAND_IORemap(NAND_IO_BASE_ADDR0, 4096);
	NandIOBase[1] = (ulong)NAND_IORemap(NAND_IO_BASE_ADDR1, 4096);
	NandIndex     = 0;

	/*init pin*/
	NAND_PIORequest(NandIndex);

	/*init clk*/
	NAND_ClkRequest(NandIndex);

	NFC_SetEccMode(0);

	/*init nand control machine*/
	ret = NFC_ChangMode(nand_info);

	return ret;
}

/*******************************************************************************
*								NFC_Exit
*
* Description 	: free hardware resource, free dma , disable NFC.
* Arguments	: nand_info -- structure with flash bus width,pagesize ,serial access mode and other configure parametre

* Returns		: 0 = sucess -1 = fail
* Notes		: .
********************************************************************************/
void NFC_Exit(void)
{
	__u32 cfg;
	/*disable NFC*/
	cfg = NFC_READ_REG(NFC_REG_CTL);
	cfg &= ((~NFC_EN) & 0xffffffff);
	NFC_WRITE_REG(NFC_REG_CTL, cfg);

	/*init clk*/
	NAND_ClkRelease(NandIndex);

	/*init pin*/
	NAND_PIORelease(NandIndex);
}

__s32 _vender_get_param(__u8 *para, __u8 *addr, __u32 count)
{
	__u32 i, cfg;
	__u32 cmd_r = 0;
	__s32 ret   = 0;

	if (read_retry_mode < 0x10) {
		/*hynix mode*/
		cmd_r = 0x37;
	}
#ifdef SUPPORT_READ_RETRY_TOSHIBA
	else if ((read_retry_mode >= 0x10) &&
		 (read_retry_mode < 0x20)) {
		/*toshiba mode*/
		return ret;
	}
#endif

	for (i = 0; i < count; i++) {
		_set_addr(&addr[i], 1);

		/*set data cnt*/
		NFC_WRITE_REG(NFC_REG_CNT, 1);

		/*set NFC_REG_CMD*/
		cfg = cmd_r;
		cfg |= (NFC_SEND_ADR | NFC_DATA_TRANS | NFC_SEND_CMD1);
		NFC_WRITE_REG(NFC_REG_CMD, cfg);

		ret = _wait_cmdfifo_free();
		ret |= _wait_cmd_finish();

		if (ret) {
			return ret;
		}
		*(para + i) = NFC_READ_RAM_B(NFC_RAM0_BASE);
	}

	return ret;
}

__s32 _vender_set_param(__u8 *para, __u8 *addr, __u32 count)
{
	__u32 i, cfg;
	__u32 cmd_w, cmd_end, cmd_done0, cmd_done1;
	__s32 ret = 0;

	/*NAND_Print("_vender_set_param, read retry mode 0x%x\n", read_retry_mode);*/

	if (read_retry_mode < 0x10) {
		/*hynix mode*/
		cmd_w     = 0x36;
		cmd_end   = 0x16;
		cmd_done0 = 0xff;
		cmd_done1 = 0xff;
	} else if ((read_retry_mode >= 0x40) &&
		   (read_retry_mode < 0x50)) {
		/*micron mode*/
		cmd_w     = 0xef;
		cmd_end   = 0xff;
		cmd_done0 = 0xff;
		cmd_done1 = 0xff;
	} else {
		return -1;
	}

	for (i = 0; i < count; i++) {
		if ((read_retry_mode >= 0x0) && (read_retry_mode < 0x10)) {
			/* send cmd to set param */
			NFC_WRITE_RAM_B(NFC_RAM0_BASE, para[i]);
			_set_addr(&addr[i], 1);
			NFC_WRITE_REG(NFC_REG_CNT, 1);

			/*set NFC_REG_CMD*/
			cfg = cmd_w;
			cfg |= (NFC_SEND_ADR | NFC_DATA_TRANS | NFC_ACCESS_DIR |
				NFC_SEND_CMD1);
			NFC_WRITE_REG(NFC_REG_CMD, cfg);

			/*NAND_Print("addr 0x%x  val 0x%x\n", addr[i], para[i]);*/
		} else if ((read_retry_mode >= 0x40) &&
			   (read_retry_mode < 0x50)) {
			/*micron read retry mode*/
			/* send cmd to set param*/
			NFC_WRITE_RAM_B(NFC_RAM0_BASE + 0, para[i]);
			NFC_WRITE_RAM_B(NFC_RAM0_BASE + 1, 0x0);
			NFC_WRITE_RAM_B(NFC_RAM0_BASE + 2, 0x0);
			NFC_WRITE_RAM_B(NFC_RAM0_BASE + 3, 0x0);
			NFC_WRITE_REG(NFC_REG_CNT, 4);

			/* set NFC_REG_CMD*/
			cfg = cmd_w;
			cfg |= (NFC_SEND_ADR | NFC_DATA_TRANS | NFC_ACCESS_DIR |
				NFC_SEND_CMD1 | NFC_WAIT_FLAG);
			_set_addr(&addr[i], 1);
			NFC_WRITE_REG(NFC_REG_CMD, cfg);

			/*NAND_Print("addr 0x%x  val 0x%x\n", addr[i], para[i]);*/
		} else {
			return -1;
		}

		ret = _wait_cmdfifo_free();
		ret |= _wait_cmd_finish();

		if (ret) {
			return ret;
		}

		/* send cmd to end */
		if (cmd_end != 0xff) {
			/*set NFC_REG_CMD*/
			cfg = cmd_end;
			cfg |= (NFC_SEND_CMD1);
			NFC_WRITE_REG(NFC_REG_CMD, cfg);

			ret = _wait_cmdfifo_free();
			ret |= _wait_cmd_finish();

			if (ret) {
				return ret;
			}
		}
	}

	if (cmd_done0 != 0xff) {
		/*set NFC_REG_CMD*/
		cfg = cmd_done0;
		cfg |= (NFC_SEND_CMD1);
		NFC_WRITE_REG(NFC_REG_CMD, cfg);

		ret = _wait_cmdfifo_free();
		ret |= _wait_cmd_finish();

		if (ret) {
			return ret;
		}
	}

	if (cmd_done1 != 0xff) {
		/*set NFC_REG_CMD*/
		cfg = cmd_done1;
		cfg |= (NFC_SEND_CMD1);
		NFC_WRITE_REG(NFC_REG_CMD, cfg);

		ret = _wait_cmdfifo_free();
		ret |= _wait_cmd_finish();

		if (ret) {
			return ret;
		}
	}

	return ret;
}

__s32 _vender_pre_condition(void)
{
	return 0;
}

__s32 _vender_get_param_otp_hynix(__u8 *para, __u8 *addr, __u32 count)
{
	__u32 i, j, cfg;
	__s32 error_flag, ret = 0;
	__u8 address[8];
	__u8 param_reverse[64];
	__u8 reg_addr[2] = { 0x0, 0x0 };
	__u8 w_data[2]   = { 0x0, 0x0 };

	if (read_retry_mode == 2) {
		reg_addr[0] = 0xFF;
		reg_addr[1] = 0xCC;
		w_data[0]   = 0x40;
		w_data[1]   = 0x4D;
	} else if (read_retry_mode == 3) {
		reg_addr[0] = 0xAE;
		reg_addr[1] = 0xB0;
		w_data[0]   = 0x00;
		w_data[1]   = 0x4D;
	} else {
		return -1;
	}

	/* send 0xFF cmd*/
	cfg = (NFC_SEND_CMD1 | NFC_WAIT_FLAG | 0xff);
	_wait_cmdfifo_free();
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	/*send cmd 0x36, addr 0xff, data 0x40*/
	NFC_WRITE_REG(NFC_REG_CNT, 1);
	NFC_WRITE_RAM_B(NFC_RAM0_BASE, w_data[0]);
	address[0] = reg_addr[0];
	_set_addr(&address[0], 1);
	cfg = (NFC_SEND_CMD1 | NFC_DATA_TRANS | NFC_ACCESS_DIR | NFC_SEND_ADR |
	       0x36);
	_wait_cmdfifo_free();
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	/*send addr 0xCC*/
	address[0] = reg_addr[1];
	_set_addr(&address[0], 1);
	cfg = (NFC_SEND_ADR);
	_wait_cmdfifo_free();
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	/*send data 0x4D*/
	NFC_WRITE_REG(NFC_REG_CNT, 1);
	NFC_WRITE_RAM_B(NFC_RAM0_BASE, w_data[1]);
	cfg = (NFC_DATA_TRANS | NFC_ACCESS_DIR);
	_wait_cmdfifo_free();
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	/*send cmd 0x16, 0x17, 0x04, 0x19, 0x00*/
	_wait_cmdfifo_free();
	cfg = (NFC_SEND_CMD1 | 0x16);
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();
	_wait_cmdfifo_free();
	cfg = (NFC_SEND_CMD1 | 0x17);
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();
	_wait_cmdfifo_free();
	cfg = (NFC_SEND_CMD1 | 0x04);
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();
	_wait_cmdfifo_free();
	cfg = (NFC_SEND_CMD1 | 0x19);
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	_wait_cmdfifo_free();
	cfg = (NFC_SEND_CMD1 | 0x00);
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	/*send addr 00, 00, 00, 02, 00*/
	address[0] = 0x00;
	address[1] = 0x00;
	address[2] = 0x00;
	address[3] = 0x02;
	address[4] = 0x00;
	_set_addr(&address[0], 5);
	cfg = (NFC_SEND_ADR | (0x4 << 16));
	_wait_cmdfifo_free();
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	/*send cmd 0x30, read data*/
	_wait_cmdfifo_free();

	NFC_WRITE_REG(NFC_REG_CNT, 2);
	cfg = (NFC_SEND_CMD1 | NFC_WAIT_FLAG | NFC_DATA_TRANS | 0x30);
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();
	/*get param data*/
	if ((NFC_READ_RAM_B(NFC_RAM0_BASE) != 0x08) ||
	    ((NFC_READ_RAM_B(NFC_RAM0_BASE + 1) != 0x08))) {
		ret = -1;
	}

	_wait_cmdfifo_free();
	NFC_WRITE_REG(NFC_REG_CNT, 1024);
	cfg = (NFC_DATA_TRANS);
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	for (j = 0; j < 8; j++) {
		error_flag = 0;
		for (i = 0; i < 64; i++) {
			para[i] = NFC_READ_RAM_B(NFC_RAM0_BASE + 128 * j + i);
			param_reverse[i] = NFC_READ_RAM_B(NFC_RAM0_BASE +
							  128 * j + 64 + i);
			if ((para[i] + param_reverse[i]) != 0xff) {
				error_flag = 1;
				break;
			}
		}
		if (!error_flag) {
			break;
		}
	}

	if (error_flag)
		ret = -1;

	/* send 0xFF cmd*/
	cfg = (NFC_SEND_CMD1 | NFC_WAIT_FLAG | 0xff);
	_wait_cmdfifo_free();
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	/* send 0x38 cmd*/
	cfg = (NFC_SEND_CMD1 | NFC_WAIT_FLAG | 0x38);
	_wait_cmdfifo_free();
	NFC_WRITE_REG(NFC_REG_CMD, cfg);
	_wait_cmd_finish();

	return ret;
}

__s32 _major_check_byte(__u8 *out, __u32 mode, __u32 level, __u8 *in,
			__u8 *in_inverse, __u32 len)
{
	__u32 bit, byte;
	__u32 cnt_1; /* the total number of bit '1' on specified bit position in all input bytes */
	__u32 cnt_0;
	__u32 get_bit, get_total_bit;
	__u8 byte_ok = 0;

	if (level < len / 2) {
		PRINT("_major_check_byte, wrong input para, level %d, len %d\n",
		      level, len);
		*out = 0xff;
		return -1;
	}

	get_total_bit = 0;
	for (bit = 0; bit < 8; bit++) {
		cnt_1   = 0;
		cnt_0   = 0;
		get_bit = 0;
		for (byte = 0; byte < len; byte++) {
			if (in[byte] & (1U << bit))
				cnt_1++;
			else
				cnt_0++;
		}

		if (cnt_1 > level) {
			byte_ok |= (1U << bit);
			get_bit = 1;
		}

		if (cnt_0 > level) {
			get_bit = 1;
		}

		if ((get_bit == 0) && (mode == 1)) {
			cnt_1   = 0;
			cnt_0   = 0;
			get_bit = 0;
			for (byte = 0; byte < len; byte++) {
				if (in_inverse[byte] & (1U << bit))
					cnt_0++;
				else
					cnt_1++;
			}

			if (cnt_0 > level) {
				get_bit = 1;
			}
			if (cnt_1 > level) {
				byte_ok |= (1U << bit);
				get_bit = 1;
			}
		}

		if (get_bit)
			get_total_bit++;
		else {
			PRINT("get bit %d failed!\n", bit);
			break;
		}
	}

	if (get_total_bit == 8) {
		*out = byte_ok;
		return 0;
	} else {
		*out = 0xff;
		return -1;
	}
}

__s32 _get_read_retry_cfg(__u8 *rr_cnt, __u8 *rr_reg_cnt, __u8 *rr_tab,
			  __u8 *otp)
{
	__s32 err_flag = 0, ret = 0;
	__u32 i, nbyte, nset;
	__u8 buf[32] = { 0 }, buf_inv[32] = { 0 };
	__u32 rr_tab_size = 32; /*RR_CNT_IN_OTP * RR_REG_CNT_IN_OTP*/

	/* read retry count */
	for (i	 = 0; i < 8; i++)
		buf[i] = otp[i];

	ret = _major_check_byte(rr_cnt, 0, 4, buf, buf_inv, 8);
	if (ret < 0) {
		PRINT("_get_read_retry_parameters, get rr count failed!\n");
		return -1;
	} else
		PRINT("rr cnt: %d\n", (*rr_cnt));

	/* read retry register count */
	for (i	 = 0; i < 8; i++)
		buf[i] = otp[8 + i];

	ret = _major_check_byte(rr_reg_cnt, 0, 4, buf, buf_inv, 8);
	if (ret < 0) {
		PRINT("_get_read_retry_parameters, get rr reg count failed!\n");
		return -1;
	} else
		PRINT("rr reg cnt: %d\n", (*rr_reg_cnt));

	if (((*rr_cnt) != 8) || ((*rr_reg_cnt) != 4)) {
		PRINT("read retry value from otp error: rr_cnt %d rr_reg_cnt %d!\n",
		      (*rr_cnt), (*rr_reg_cnt));
		return -1;
	}

	/* read retry table */
	for (nbyte = 0; nbyte < rr_tab_size; nbyte++) {
		for (nset = 0; nset < 8; nset++) {
			buf[nset]     = 0;
			buf_inv[nset] = 0;
			buf[nset] = otp[16 + nset * rr_tab_size * 2 + nbyte];
			buf_inv[nset] = otp[16 + nset * rr_tab_size * 2 +
					    rr_tab_size + nbyte];
		}

		ret = _major_check_byte(&rr_tab[nbyte], 1, 4, buf, buf_inv, 8);
		if (ret < 0) {
			PRINT("_get_read_retry_parameters, get the %d-th byte of rr table failed!\n",
			      nbyte);
			err_flag = 1;
			break;
		}
	}
	if (err_flag)
		ret = -1;
	else
		ret = 0;

	return ret;
}

__s32 _read_otp_info_hynix(__u32 chip, __u8 *otp_chip)
{
	/*__u32 rb_index;*/
	__u32 i, ndie = 0; /*j*/
	__u8 *otp;
	__u8 abuf[8] = { 0 };
	__u32 cfg;

	/*for (ndie = 0; ndie < 1; ndie++) */{
		otp = otp_chip;
		if (otp == NULL) {
			PRINT("invalid buffer for otp info!\n");
			return -1;
		}

		/* send 0xFF cmd*/
		cfg = 0;
		cfg = (NFC_SEND_CMD1 | NFC_WAIT_FLAG | 0xff);
		_wait_cmdfifo_free();
		NFC_WRITE_REG(NFC_REG_CMD, cfg);
		_wait_cmd_finish();

		if ((ndie == 1) || (ndie == 2) || (ndie == 3)) {
			abuf[0] = 0x00;
			abuf[1] = 0x00;
			if (ndie == 1)
				abuf[2] = 0x10;
			else if (ndie == 2)
				abuf[2] = 0x20;
			else if (ndie == 3)
				abuf[2] = 0x30;
			else
				PRINT("=======wrong ndie %d\n", ndie);

			/*send cmd 0x78, abuf*/
			cfg = 0;
			_set_addr(&abuf[0], 3);
			cfg = (NFC_SEND_CMD1 | NFC_SEND_ADR | (0x2 << 16) |
			       0x78);
			_wait_cmdfifo_free();
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();
		}

		{
			cfg     = 0;
			abuf[0] = 0x38;
			/*send cmd 0x36, addr 0x38, data 0x52*/
			NFC_WRITE_REG(NFC_REG_CNT, 1);
			NFC_WRITE_RAM_B(NFC_RAM0_BASE, 0x52);
			_set_addr(&abuf[0], 1);
			cfg = (NFC_SEND_CMD1 | NFC_DATA_TRANS | NFC_ACCESS_DIR |
			       NFC_SEND_ADR | 0x36);
			_wait_cmdfifo_free();
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();

			/*send cmd 0x16, 0x17, 0x04, 0x19, 0x00*/
			cfg = 0;
			_wait_cmdfifo_free();
			cfg = (NFC_SEND_CMD1 | 0x16);
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();
			cfg = 0;
			_wait_cmdfifo_free();
			cfg = (NFC_SEND_CMD1 | 0x17);
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();
			cfg = 0;
			_wait_cmdfifo_free();
			cfg = (NFC_SEND_CMD1 | 0x04);
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();
			cfg = 0;
			_wait_cmdfifo_free();
			cfg = (NFC_SEND_CMD1 | 0x19);
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();

			cfg = 0;
			_wait_cmdfifo_free();
			cfg = (NFC_SEND_CMD1 | 0x00);
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();

			/*send addr 00, 00, 00, 02*/
			cfg     = 0;
			abuf[0] = 0x00;
			abuf[1] = 0x00;
			abuf[2] = 0x00;
			abuf[3] = 0x02;
			_set_addr(&abuf[0], 4);
			cfg = (NFC_SEND_ADR | (0x3 << 16));
			_wait_cmdfifo_free();
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();
		}

		{
			if (ndie == 0)
				abuf[0] = 0x00;
			else if (ndie == 1)
				abuf[0] = 0x10;
			else if (ndie == 2)
				abuf[0] = 0x20;
			else if (ndie == 3)
				abuf[0] = 0x30;
			else
				PRINT("!!!wrong ndie %d\n", ndie);
			/*send addr*/
			cfg = 0;
			_set_addr(&abuf[0], 1);
			cfg = (NFC_SEND_ADR);
			_wait_cmdfifo_free();
			NFC_WRITE_REG(NFC_REG_CMD, cfg);
			_wait_cmd_finish();
		}

		/*send cmd 0x30, read data*/
		_wait_cmdfifo_free();
		cfg = 0;
		NFC_WRITE_REG(NFC_REG_CNT, 528);
		cfg = (NFC_SEND_CMD1 | NFC_WAIT_FLAG | NFC_DATA_TRANS | 0x30);
		NFC_WRITE_REG(NFC_REG_CMD, cfg);
		_wait_cmd_finish();

		/* read otp data from ndfc fifo */
		for (i = 0; i < 528; i++) {
			otp[i] = NFC_READ_RAM_B(NFC_RAM0_BASE + i);
		}

		/* send 0xFF cmd*/
		cfg = 0;
		cfg = (NFC_SEND_CMD1 | NFC_WAIT_FLAG | 0xff);
		_wait_cmdfifo_free();
		NFC_WRITE_REG(NFC_REG_CMD, cfg);
		_wait_cmd_finish();

		abuf[0] = 0x38;
		/*send cmd 0x36, addr 0x38, data 0x00*/
		NFC_WRITE_REG(NFC_REG_CNT, 1);
		NFC_WRITE_RAM_B(NFC_RAM0_BASE, 0x00);
		_set_addr(&abuf[0], 1);
		cfg = 0;
		cfg = (NFC_SEND_CMD1 | NFC_DATA_TRANS | NFC_ACCESS_DIR |
		       NFC_SEND_ADR | 0x36);
		_wait_cmdfifo_free();
		NFC_WRITE_REG(NFC_REG_CMD, cfg);
		_wait_cmd_finish();

		/*send 0x16 cmd*/
		_wait_cmdfifo_free();
		cfg = 0;
		cfg = (NFC_SEND_CMD1 | 0x16);
		NFC_WRITE_REG(NFC_REG_CMD, cfg);
		_wait_cmd_finish();

		/* dummy read(address don't care) 0x00 cmd + 0x0 addr + 0x30 cmd*/
		abuf[0] = 0x00;
		abuf[1] = 0x00;
		abuf[2] = 0x00;
		abuf[3] = 0x00;
		abuf[4] = 0x00;
		_set_addr(&abuf[0], 5);
		cfg = 0;
		cfg = (NFC_SEND_CMD1 | NFC_SEND_ADR | (0x4 << 16) | 0x00);
		_wait_cmdfifo_free();
		NFC_WRITE_REG(NFC_REG_CMD, cfg);
		_wait_cmd_finish();

		/*send 0x30 cmd*/
		_wait_cmdfifo_free();
		cfg = 0;
		cfg = (NFC_SEND_CMD1 | NFC_WAIT_FLAG | 0x30);
		NFC_WRITE_REG(NFC_REG_CMD, cfg);
		_wait_cmd_finish();
	}
	return 0;
}

__s32 _get_rr_value_otp_hynix(__u32 nchip)
{
	__s32 ret = 0;
	__u8 *otp_info_hynix_16nm;
	__u8 rr_cnt_hynix_16nm;
	__u8 rr_reg_cnt_hynix_16nm;

	otp_info_hynix_16nm = MALLOC(528);
	if (!otp_info_hynix_16nm) {
		PRINT("otp_info_hynix_16nm : allocate memory fail\n");
		return -1;
	}

	ret = _read_otp_info_hynix(nchip, otp_info_hynix_16nm);
	if (ret < 0) {
		PRINT("chip %d get otp info failed!\n", nchip);
	}

	ret = _get_read_retry_cfg(&rr_cnt_hynix_16nm, &rr_reg_cnt_hynix_16nm,
				  &hynix16nm_read_retry_otp_value[nchip][0][0],
				  otp_info_hynix_16nm);
	if (ret < 0) {
		PRINT(" chip %d get read retry cfg from otp info failed!\n",
		      nchip);
	}

	FREE(otp_info_hynix_16nm, 528);

	return ret;
}

/*for offset from defaul value*/
__s32 NFC_ReadRetry(__u32 chip, __u32 retry_count, __u32 read_retry_type)
{
	__u32 i;
	__s32 ret = 0;
	__s16 temp_val;
	__u8 param[READ_RETRY_MAX_REG_NUM];

	if (retry_count > read_retry_cycle)
		return -1;

	if (read_retry_mode < 0x10) {
		/*for hynix read retry mode*/
		if ((read_retry_mode == 0) || (read_retry_mode == 1)) {
			if (retry_count == 0)
				ret = _vender_set_param(
					&read_retry_default_val[chip][0],
					&read_retry_reg_adr[0],
					read_retry_reg_num);
			else {
				for (i = 0; i < read_retry_reg_num; i++) {
					temp_val =
						(read_retry_default_val[chip][i] +
						 read_retry_val[retry_count - 1]
							       [i]);
					if (temp_val > 255)
						temp_val = 0xff;
					else if (temp_val < 0)
						temp_val = 0;
					else
						temp_val &= 0xff;

					param[i] = (__u8)temp_val;
				}
				/*fix 0*/
				if ((retry_count >= 2) && (retry_count <= 6))
					param[0] = 0;

				if ((retry_count == 5) || (retry_count == 6))
					param[1] = 0;
				ret		 = _vender_set_param(&param[0],
							&read_retry_reg_adr[0],
							read_retry_reg_num);
			}

		} else if ((read_retry_mode == 2) || (read_retry_mode == 3)) {
			for (i = 0; i < read_retry_reg_num; i++)
				param[i] =
					hynix_read_retry_otp_value[chip]
								  [retry_count]
								  [i];

			ret = _vender_set_param(&param[0],
						&read_retry_reg_adr[0],
						read_retry_reg_num);
		} else if (read_retry_mode == 4) {
			for (i		 = 0; i < read_retry_reg_num; i++)
				param[i] = hynix16nm_read_retry_otp_value
					[chip][retry_count][i];

			ret = _vender_set_param(&param[0],
						&read_retry_reg_adr[0],
						read_retry_reg_num);
		}

	} else if ((read_retry_mode >= 0x40) &&
		   (read_retry_mode < 0x50)) {
		/*for micron readretry mode*/
		for (i		 = 0; i < read_retry_reg_num; i++)
			param[i] = (__u8)read_retry_val[retry_count - 1][i];
		ret = _vender_set_param(&param[0], &read_retry_reg_adr[0],
					read_retry_reg_num);
	} else {
		PHY_ERR("NFC_ReadRetry, unknown read retry mode 0x%x\n",
			read_retry_mode);
		return -1;
	}

	return ret;
}

__s32 NFC_ReadRetryInit(__u32 read_retry_type)
{
	__u32 i, j;
	/*init*/
	read_retry_mode    = (read_retry_type >> 16) & 0xff;
	read_retry_cycle   = (read_retry_type >> 8) & 0xff;
	read_retry_reg_num = (read_retry_type >> 0) & 0xff;

	/*NAND_Print("NFC_ReadRetryInit...\n");*/

	if (read_retry_mode == 0) {
		/*mode0  H27UCG8T2MYR*/
		read_retry_reg_adr[0] = 0xAC;
		read_retry_reg_adr[1] = 0xAD;
		read_retry_reg_adr[2] = 0xAE;
		read_retry_reg_adr[3] = 0xAF;

		/*set read retry level*/
		for (i = 0; i < read_retry_cycle; i++) {
			for (j = 0; j < read_retry_reg_num; j++) {
				read_retry_val[i][j] = para0[i][j];
			}
		}

	} else if (read_retry_mode == 1) {
		/*mode1  H27UBG8T2BTR*/
		read_retry_reg_adr[0] = 0xA7;
		read_retry_reg_adr[1] = 0xAD;
		read_retry_reg_adr[2] = 0xAE;
		read_retry_reg_adr[3] = 0xAF;

		/*set read retry level*/
		for (i = 0; i < read_retry_cycle; i++) {
			for (j = 0; j < read_retry_reg_num; j++) {
				read_retry_val[i][j] = para1[i][j];
			}
		}
	} else if (read_retry_mode == 2) {
		/*mode2  H27UCG8T2ATR*/
		read_retry_reg_adr[0] = 0xCC;
		read_retry_reg_adr[1] = 0xBF;
		read_retry_reg_adr[2] = 0xAA;
		read_retry_reg_adr[3] = 0xAB;
		read_retry_reg_adr[4] = 0xCD;
		read_retry_reg_adr[5] = 0xAD;
		read_retry_reg_adr[6] = 0xAE;
		read_retry_reg_adr[7] = 0xAF;
	} else if (read_retry_mode == 3) {
		/*mode3  H27UCG8T2ATR*/
		read_retry_reg_adr[0] = 0xB0;
		read_retry_reg_adr[1] = 0xB1;
		read_retry_reg_adr[2] = 0xB2;
		read_retry_reg_adr[3] = 0xB3;
		read_retry_reg_adr[4] = 0xB4;
		read_retry_reg_adr[5] = 0xB5;
		read_retry_reg_adr[6] = 0xB6;
		read_retry_reg_adr[7] = 0xB7;
	} else if (read_retry_mode == 4) {
		/*mode3  H27UCG8T2ETR*/
		read_retry_reg_adr[0] = 0x38;
		read_retry_reg_adr[1] = 0x39;
		read_retry_reg_adr[2] = 0x3A;
		read_retry_reg_adr[3] = 0x3B;

	} else if (read_retry_mode == 0x40) {
		/*mode 0x40 micron mode*/
		read_retry_reg_adr[0] = 0x89;
		for (i = 0; i < read_retry_cycle; i++) {
			for (j = 0; j < read_retry_reg_num; j++) {
				read_retry_val[i][j] = param0x40[i];
			}
		}
	} else if (read_retry_mode == 0x41) {
		/*mode 0x41 L95B_128_256_512Gb_1Tb_2Tb_Async_Sync_NAND*/
		read_retry_reg_adr[0] = 0x89;
		for (i = 0; i < read_retry_cycle; i++) {
			for (j = 0; j < read_retry_reg_num; j++) {
				read_retry_val[i][j] = param0x41[i];
			}
		}
	} else {
		PHY_ERR("NFC_ReadRetryInit, unknown read retry mode 0x%x\n",
			read_retry_mode);
		return -1;
	}

	return 0;
}

void NFC_GetOTPValue(__u32 chip, __u8 *otp_value, __u32 read_retry_type)
{
	__u8 *pdata;
	__u32 i;

	if ((read_retry_mode == 0x2) || (read_retry_mode == 0x3)) {
		pdata = (__u8 *)(&hynix_read_retry_otp_value[0][0][0]);
		for (i		 = 0; i < 64; i++)
			pdata[i] = otp_value[i];
	} else if (read_retry_mode == 0x4) {
		pdata = (__u8 *)(&hynix16nm_read_retry_otp_value[0][0][0]);
		for (i = 0; i < 32; i++) {
			pdata[i] = otp_value[i];
		}
	}
}

__s32 NFC_GetDefaultParam(__u32 chip, __u8 *default_value,
			  __u32 read_retry_type)
{
	__s32 ret;
	__u32 i, j, Count, flag;

	if (read_retry_mode < 0x10) {
		/*hynix read retry mode*/
		if ((read_retry_mode == 0x0) || (read_retry_mode == 0x1)) {
			ret = _vender_get_param(
				&read_retry_default_val[chip][0],
				&read_retry_reg_adr[0], read_retry_reg_num);
			for (i = 0; i < read_retry_reg_num; i++) {
				default_value[i] =
					read_retry_default_val[chip][i];
			}
			return ret;
		} else if ((read_retry_mode == 0x2) ||
			   (read_retry_mode == 0x3)) {
			for (Count = 0; Count < 5; Count++) {
				PRINT("_vender_get_param_otp_hynix time %d!\n",
				      Count);
				ret = _vender_get_param_otp_hynix(
					&hynix_read_retry_otp_value[chip][0][0],
					&read_retry_reg_adr[0], 64);
				if (!ret)
					break;
			}
			if (ret)
				PRINT("_vender_get_param_otp_hynix error!\n");

			/*set read retry level*/
			for (i = 0; i < 8; i++) {
				for (j = 0; j < 8; j++) {
					default_value[8 * i + j] =
						hynix_read_retry_otp_value[chip]
									  [i][j];
				}
			}
		} else if (read_retry_mode == 0x4) {
			Count = 0;
			flag  = 0;
			/*for(Count =0; Count<5; Count++)*/
			while (flag == 0) {
				PHY_DBG("_vender_get_param_otp_hynix time %d!\n",
					Count);
				ret = _get_rr_value_otp_hynix((__u8)chip);
				if (ret == 0) {
					flag = 1;
				}
				Count++;
			}

			for (i = 0; i < 8; i++) {
				for (j = 0; j < 4; j++) {
					default_value[4 * i + j] =
						hynix16nm_read_retry_otp_value
							[0][i][j];
				}
			}
		}
	}

	return 0;
}

__s32 NFC_SetDefaultParam(__u32 chip, __u8 *default_value,
			  __u32 read_retry_type)
{
	__s32 ret;
	__u32 i;

	if (read_retry_mode < 0x10) {
		/*hynix read retry mode*/
		for (i = 0; i < read_retry_reg_num; i++) {
			if ((read_retry_mode == 0x0) ||
			    (read_retry_mode == 0x1))
				default_value[i] =
					read_retry_default_val[chip][i];
			else if ((read_retry_mode == 0x2) ||
				 (read_retry_mode == 0x3))
				default_value[i] =
					hynix_read_retry_otp_value[chip][0][i];
			else if (read_retry_mode == 0x4)
				default_value[i] =
					hynix16nm_read_retry_otp_value[chip][0]
								      [i];
		}
		ret = _vender_set_param(default_value, &read_retry_reg_adr[0],
					read_retry_reg_num);
		return ret;
	} else if ((read_retry_mode >= 0x40) &&
		   (read_retry_mode < 0x50)) {
		/*micron read retry mode*/
		for (i = 0; i < read_retry_reg_num; i++) {
			default_value[i] = 0x0;
		}
		ret = _vender_set_param(default_value, &read_retry_reg_adr[0],
					read_retry_reg_num);

		return ret;
	} else {
		PHY_ERR("NFC_SetDefaultParam, unknown read retry mode 0x%x\n",
			read_retry_mode);
		return 0;
	}

	return 0;
}

__s32 NFC_ReadRetryExit(__u32 read_retry_type)
{
	return 0;
}

#endif /* _NFC_BOOT0_H_ */
