/*******************************************************************************
*函数名称: NF_read
*函数原型:int32 NF_read( __u32 sector_num, void *buffer, __u32 N )
*函数功能: 读取nand flash中的数据放入内存中。
*入口参数: sector_num         flash中起始sector number
*          buffer             内存中的起始地址
*          N                  读取的sector个数(以sector为单位)
*返 回 值: NF_OK              正确读取
*          NF_OVERTIME_ERR    操作超时
*          NF_ERROR           读取有误
*备    注: sector_num必须是某个page内的第一个sector. 不能跨块操作。
*******************************************************************************/
__s32 NF_read( __u32 sector_num, void *buffer, __u32 N )
{
	struct boot_physical_param  para;
	__u8  oob_buf[MAX_PAGE_SIZE/NF_SECTOR_SIZE * OOB_BUF_SIZE_PER_SECTOR];
	__u32 page_nr;
	__u32 scts_per_page = NF_PAGE_SIZE >> NF_SCT_SZ_WIDTH;
	__u32 residue;
	__u32 start_page;
	__u32 start_sct;
	__u32 i;
	__u32 blk_num;

	para.chip = 0;
	start_sct = nand_g_mod( sector_num, NF_BLOCK_SIZE >> NF_SCT_SZ_WIDTH, &blk_num );
	para.block = blk_num;
	if( nand_g_mod( start_sct, NF_PAGE_SIZE >> NF_SCT_SZ_WIDTH, &start_page ) != 0 )
		return NF_ERROR;
	para.oobbuf = oob_buf;
	residue = nand_g_mod( N, scts_per_page, &page_nr );
	for( i = 0; i < page_nr; i++ )
	{
		para.mainbuf = (__u8 *)buffer + NF_PAGE_SIZE * i;
		para.page = start_page + i;
		if( NAND_PhyRead( &para ) == FAIL )
			return NF_ERROR;
	}
	if( residue != 0 )
	{
		__u8  page_buf[MAX_PAGE_SIZE];
		__u32 j;
		__u32 word_nr;
		__u32 *p;
		__u32 *q;

		para.mainbuf = page_buf;
		para.page = page_nr;
		if( NAND_PhyRead( &para ) == FAIL )
			return NF_ERROR;
		for( j = 0, p = (__u32 *)( (__u32)buffer + NF_PAGE_SIZE * page_nr ), word_nr = residue * NF_SECTOR_SIZE >> 2, q = (__u32 *)page_buf;
			 j < word_nr;
			 j++ )
			*p++ = *q++;
	}
/*******************************************************************************
*函数名称: NAND_BadBlockScan
*函数原型:bad_blcok_scan(const boot_nand_para_t *nand_param)
*函数功能: 标记blk_num指定的块为坏块。
*入口参数: nand_param
*返 回 值: >0              编程操作成功
*          -1              编程操作失败
*备    注:
*******************************************************************************/
__s32 NAND_BadBlockScan(const boot_nand_para_t *nand_param)
{
	boot_flash_info_t info;
	struct boot_physical_param  para;

	__s32  i, j, k;
	__u32  page_with_bad_block, page_per_block;
	__s32  page_index[4];
	__u32  bad_block_cnt[8];
	__u32  bad_block_num = 0;
	__u32  good_block_num = 0;
	__s32  good_block_ratio = -1;
	__u32  chip;
	__u8   oob_buf[OOB_BUF_SIZE];
	__u8*  page_buf;
	__u32  die_skip_flag = (nand_param->OperationOpt)&(0x1<<11);
	__u32  block_cnt_of_die = (nand_param->BlkCntPerDie);

	for(i=0; i<8; i++)
	    bad_block_cnt[i] = 0;

	NAND_Print("Ready to scan bad blocks.\n");

    // get nand info to cal
	NAND_Print("nfb phy init ok.\n");
	if( NAND_GetFlashInfo( &info ) )
	    {
    		NAND_Print("get flash info failed.\n");
    		return -1;
    	}
	NAND_Print("Succeed in getting flash info.\n");

	//cal nand parameters
	//page_buf = (__u8*)(BAD_BLK_SCAN_BUF_ADR);
    page_buf = (__u8*)wBoot_malloc(32 * 1024);
    if(!page_buf)
    {
        NAND_Print("malloc memory for page buf fail\n");
        return -1;
    }

	page_with_bad_block = info.pagewithbadflag;
	page_per_block = info.blocksize/info.pagesize;

	//read the first, second, last, last-1 page for check bad blocks
	page_index[0] = 0;
	page_index[1] = 0xEE;
	page_index[2] = 0xEE;
	page_index[3] = 0xEE;

    switch(page_with_bad_block & 0x03)
    {
        case 0x00:
            //the bad block flag is in the first page, same as the logical information, just read 1 page is ok
            break;

        case 0x01:
            //the bad block flag is in the first page or the second page, need read the first page and the second page
            page_index[1] = 1;
            break;

        case 0x02:
            //the bad block flag is in the last page, need read the first page and the last page
            page_index[1] = page_per_block - 1;
            break;

        case 0x03:
            //the bad block flag is in the last 2 page, so, need read the first page, the last page and the last-1 page
            page_index[1] = page_per_block - 1;
            page_index[2] = page_per_block - 2;
            break;
    }

    //scan bad blocks
	for( i = 0;  i < info.chip_cnt;  i++ ){

		chip = _cal_real_chip( i, nand_param->ChipConnectInfo );
		NAND_Print("scan CE %u\n", chip);
		bad_block_cnt[chip] = 0;

		for( j = 0;  j < info.blk_cnt_per_chip;  j++ )
		{
			para.chip = chip;
			if(!die_skip_flag)
			    para.block = j;
			else
			    para.block = j%block_cnt_of_die + 2*block_cnt_of_die*(j/block_cnt_of_die);
			para.mainbuf = page_buf;
			para.oobbuf = oob_buf;

			for(k = 0; k<4; k++)
			{
				// read pages for check
				para.page = page_index[k];
				if(para.page == 0xEE)
				    continue;
				NAND_PhyRead(&para );

				// find bad blocks
				if(oob_buf[0] != 0xff)
				{
				    NAND_Print("find defined bad block in chip %u, block %u.\n", i, j);
					bad_block_cnt[chip]++;
                    break;
				}
			}
		}
	}

	// cal bad block num
	if(info.chip_cnt == 0x1)        //for one CE
    	{
    	    if(nand_param->ChipConnectInfo == 0x1)
    	        {
    	            bad_block_num = bad_block_cnt[0]<<1;
    	        }
    		else
    		    {
    		        NAND_Print("chip connect parameter %x error \n", nand_param->ChipConnectInfo);
    		        wBoot_free(page_buf);

        			return -1;
    		    }
    	}
	else if(info.chip_cnt == 2)     //for two CE
    	{
    		if(nand_param->ChipConnectInfo == 0x3)
    		    {
    			    bad_block_num = (bad_block_cnt[0] + bad_block_cnt[1])<<1;
    		    }
    		else if(nand_param->ChipConnectInfo == 0x5)
        		{
        			bad_block_num = (bad_block_cnt[0] + bad_block_cnt[2])<<1;
        		}
    		else if(nand_param->ChipConnectInfo == 0x81)
        		{
        			bad_block_num = (bad_block_cnt[0] + bad_block_cnt[7])<<1;
        		}
    		else
        		{
        			NAND_Print("chip connect parameter %x error \n", nand_param->ChipConnectInfo);
        			wBoot_free(page_buf);

        			return -1;
        		}
    	}
	else if(info.chip_cnt == 4)     //for four CE
	    {
    		if(nand_param->ChipConnectInfo == 0xf)
    		    {
    			    bad_block_num = max_badblk((bad_block_cnt[0] + bad_block_cnt[2]),(bad_block_cnt[1] + bad_block_cnt[3]))<<1;
    		    }
    		else if(nand_param->ChipConnectInfo == 0x55)
    		    {
    			    bad_block_num = max_badblk((bad_block_cnt[0] + bad_block_cnt[2]),(bad_block_cnt[4] + bad_block_cnt[6]))<<1;
    		    }
    		else
    		    {
    			    NAND_Print("chip connect parameter %x error \n",nand_param->ChipConnectInfo);
    			    wBoot_free(page_buf);

    			    return -1;
    		    }

	    }
	else if(info.chip_cnt == 8)     //for eight CE
	    {
    		if(nand_param->ChipConnectInfo == 0xff)
    		    {
        			bad_block_num = max_badblk((bad_block_cnt[0] + bad_block_cnt[2]),(bad_block_cnt[1] + bad_block_cnt[3]));
        			bad_block_num = 2*max_badblk(bad_block_num, max_badblk((bad_block_cnt[4] + bad_block_cnt[6]),(bad_block_cnt[5] + bad_block_cnt[7])));

        		}
    		else
    		    {
        			NAND_Print("chip connect parameter %x error \n",nand_param->ChipConnectInfo);
        			wBoot_free(page_buf);

        			return -1;
        		}
	    }
	else
	    {
	        NAND_Print("chip cnt parameter %x error \n",nand_param->ChipCnt);
	        wBoot_free(page_buf);

        	return -1;
	    }


	//cal good block num required per 1024 blocks
	good_block_num = (1024*(info.blk_cnt_per_chip - bad_block_num))/info.blk_cnt_per_chip -114;
    for(i=0; i<info.chip_cnt; i++)
    {
		chip = _cal_real_chip( i, nand_param->ChipConnectInfo );
		NAND_Print(" %d bad blocks in CE %u \n", bad_block_cnt[chip], chip);
	}
	NAND_Print("cal bad block num is %u \n", bad_block_num);
	NAND_Print("cal good block num is %u \n", good_block_num);

    //cal good block ratio
	for(i=0; i<10; i++)
	{
		if(good_block_num >= (nand_param->good_block_ratio - 32*i))
		    {
    			good_block_ratio = (nand_param->good_block_ratio - 32*i);
    			NAND_Print("good block ratio is %u \n",good_block_ratio);
    			break;
    		}
	}
    wBoot_free(page_buf);

 	return good_block_ratio;
}
/*
************************************************************************************************************************
*                       NAND_EraseChip
*
*Description: Erase chip, only erase the all 0x0 blocks
*
*Arguments  : connecnt info.
*
*Return     :   = 0     OK;
*               other  Fail.
************************************************************************************************************************
*/
__s32  NAND_EraseChip( const boot_nand_para_t *nand_param)
{
	boot_flash_info_t info;
	struct boot_physical_param  para_read;

	__s32  i,j,k,m;
	__s32  ret;
	__s32  cnt0, cnt1;
	__s32  mark_err_flag;
	__u32  bad_block_flag;
	__u32  page_with_bad_block, page_size, page_per_block;
	__s32  page_index[4];
	__u32  chip;
	__u8   oob_buf_read[OOB_BUF_SIZE];
	__u8*  page_buf_read;
	__s32  error_flag = 0;
	__s32  block_start;
	__u32  die_skip_flag = (nand_param->OperationOpt)&(0x1<<11);
	__u32  block_cnt_of_die = (nand_param->BlkCntPerDie);

    bad_block_scan_flag = 0;

    page_buf_read = (__u8*)wBoot_malloc(32 * 1024);
    if(!page_buf_read)
    {
        NAND_Print("malloc memory for page read fail\n");
        return -1;
    }
	NAND_Print("Ready to erase chip.\n");
	// get nand info to cal
	NAND_Print("nfb phy init ok.\n");
	if( NAND_GetFlashInfo( &info ) != 0 )
	{
		NAND_Print("get flash info failed.\n");
		wBoot_free(page_buf_read);
		return -1;
	}
	NAND_Print("Succeed in getting flash info.\n");

	page_size = 512*info.pagesize;
	page_with_bad_block = info.pagewithbadflag;
	page_per_block = info.blocksize/info.pagesize;
	NAND_Print("page size:%x, block size: %x, bad block position: %x.\n",page_size, page_per_block,page_with_bad_block);

    page_index[0] = 0;
	page_index[1] = 0xEE;
	page_index[2] = 0xEE;
	page_index[3] = 0xEE;

    switch(page_with_bad_block & 0x03)
    {
        case 0x00:
            //the bad block flag is in the first page, same as the logical information, just read 1 page is ok
            break;

        case 0x01:
            //the bad block flag is in the first page or the second page, need read the first page and the second page
            page_index[1] = 1;
            break;

        case 0x02:
            //the bad block flag is in the last page, need read the first page and the last page
            page_index[1] = page_per_block - 1;
            break;

        case 0x03:
            //the bad block flag is in the last 2 page, so, need read the first page, the last page and the last-1 page
            page_index[1] = page_per_block - 1;
            page_index[2] = page_per_block - 2;
            break;
    }

    NAND_Print("chip_cnt = %x, chip_connect = %x, rb_cnt = %x,  rb_connect = %x,  good_block_ratio =%x \n",nand_param->ChipCnt,nand_param->ChipConnectInfo,nand_param->RbCnt,nand_param->RbConnectInfo,nand_param->good_block_ratio);

	for( i = 0;  i < nand_param->ChipCnt;  i++ )
	{
	    //select chip
		chip = _cal_real_chip( i, nand_param->ChipConnectInfo );
        NAND_Print("erase chip %u \n", chip);

        //scan for bad blocks, only erase good block, all 0x00 blocks is defined bad blocks
		if(i == 0)
		{
			block_start = 7;
		}
		else
		{
			block_start = 0;
		}
		for( j = block_start;  j < info.blk_cnt_per_chip;  j++ )
		{
			para_read.chip = chip;
			if(!die_skip_flag)
			    para_read.block = j;
			else
			    para_read.block = j%block_cnt_of_die + 2*block_cnt_of_die*(j/block_cnt_of_die);
			para_read.mainbuf = page_buf_read;
			para_read.oobbuf = oob_buf_read;

			bad_block_flag = 0;

			for(k = 0; k<4; k++)
			{
				cnt0 =0;
				cnt1 =0;
				para_read.page = page_index[k];
				if( para_read.page== 0xEE)
				    break;

				ret = NAND_PhyRead(& para_read );

				//check the current block is a all 0x00 block
				#if 0
				for(m=0; m<8; m++)   //check user data, 8 byte
			    {
			        if(oob_buf_read[m] == ((__u8)0x0) )
			            cnt1++;
			        else
			            break;
			    }
				for(m=0; m<page_size; m++)  //check main data
				{
					if(page_buf_read[m] == ((__u8)0x0) )
						cnt0++;
					else
						break;
			    }

				if((cnt0 == page_size)&&(cnt1 == 8))
				{
					bad_block_flag = 1;
					NAND_Print("find a all 0x00 block %u\n", j);
					break;
				}
				#endif
				if(oob_buf_read[0] == 0x0)
				{
						bad_block_flag = 1;
						NAND_Print("find a bad block %u\n", j);
						break;
				}
			}

			if(bad_block_flag)
				continue;

			ret = NAND_PhyErase( &para_read );
			if( ret != 0 )
	    		{
	    		    NAND_Print("erasing block %u failed.\n", j );
			    	mark_err_flag = mark_bad_block( i, j );
	    		    if( mark_err_flag!= 0 )
	    		        {
        					error_flag++;
        					NAND_Print("error in marking bad block flag in chip %u, block %u, mark error flag %u.\n", i, j, mark_err_flag);
        				}
	    		}

    		}
	}

	NAND_Print("has cleared the chip.\n");
	if(error_flag)
		NAND_Print("the nand is Bad.\n");
	else
		NAND_Print("the nand is OK.\n");

    wBoot_free(page_buf_read);

	return 0;

}
/*******************************************************************************
*函数名称: mark_bad_block
*函数原型:mark_bad_block( __u32 chip_num, __u32 blk_num )
*函数功能: 标记blk_num指定的块为坏块。
*入口参数: chip_num           chip number
           blk_num            块号
*返 回 值: 0              编程操作成功
*          other          编程操作失败
*备    注:
*******************************************************************************/
static __s32 mark_bad_block( __u32 chip_num, __u32 blk_num)
{
    boot_flash_info_t info;
	struct boot_physical_param  para;
	__u8   oob_buf[OOB_BUF_SIZE];
	__u8*  page_buf;
	__s32  page_index[4];
    __u32  page_with_bad_block, page_per_block;
	__u32  i;
	__s32  mark_err_flag = -1;


	if( NAND_GetFlashInfo( &info ))
	{
		NAND_Print("get flash info failed.\n");
		return -1;
	}

	//cal nand parameters
	//page_buf = (__u8*)(MARK_BAD_BLK_BUF_ADR);
    page_buf = (__u8*)wBoot_malloc(32 * 1024);
    if(!page_buf)
    {
        NAND_Print("malloc memory for page buf fail\n");
        return -1;
    }

	page_with_bad_block = info.pagewithbadflag;
	page_per_block = info.blocksize/info.pagesize;

	//read the first, second, last, last-1 page for check bad blocks
	page_index[0] = 0;
	page_index[1] = 0xEE;
	page_index[2] = 0xEE;
	page_index[3] = 0xEE;

    switch(page_with_bad_block & 0x03)
    {
        case 0x00:
            //the bad block flag is in the first page, same as the logical information, just read 1 page is ok
            break;

        case 0x01:
            //the bad block flag is in the first page or the second page, need read the first page and the second page
            page_index[1] = 1;
            break;

        case 0x02:
            //the bad block flag is in the last page, need read the first page and the last page
            page_index[1] = page_per_block - 1;
            break;

        case 0x03:
            //the bad block flag is in the last 2 page, so, need read the first page, the last page and the last-1 page
            page_index[1] = page_per_block - 1;
            page_index[2] = page_per_block - 2;
            break;
    }

	for(i =0; i<4; i++)
	{
		oob_buf[0] = 0x0;
		oob_buf[1] = 0x1;
		oob_buf[2] = 0x2;
		oob_buf[3] = 0x3;
		oob_buf[4] = 0x89;
		oob_buf[5] = 0xab;
		oob_buf[6] = 0xcd;
		oob_buf[7] = 0xef;

		para.chip = chip_num;
		para.block = blk_num;
		para.page = page_index[i];
		para.mainbuf = page_buf;
		para.oobbuf = oob_buf;

		if(para.page == 0xEE)
		    continue;

        NAND_PhyWrite( &para );
        NAND_PhyRead( &para );

		if(oob_buf[0] !=0xff)
			mark_err_flag = 0;
	}

	wBoot_free(page_buf);

	return mark_err_flag;
}