/******************************************************************************
 * $Id: rtk_nand_sd5.c,v 1.4 2012/04/02 06:39:30 ccwei0908 Exp $
 * drivers/mtd/nand/rtk_nand.c
 * Overview: Realtek NAND Flash Controller Driver
 * Copyright (c) 2008 Realtek Semiconductor Corp. All Rights Reserved.
 * Modification History:
 *    #000 2008-05-30 CMYu   create file
 *
 *******************************************************************************/
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/pm.h>
#include <asm/io.h>
#include "bspchip.h" 
#include <soc/kernel_soc.h> //soc.h must include
#include "./rtk_nand.h" 
#include <linux/bitops.h>
#include <mtd/mtd-abi.h>
//#include <asm/r4kcache.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/root_dev.h>
#include "./rtk_nand.h"
#include "../mtdcore.h"
#include <linux/semaphore.h>
#include <linux/module.h>

#define BANNER  "Realtek Luna NAND Flash Driver"
#define VERSION  "$Id: luna_nand.c,2012/04/02 06:39:30 ccwei0908 Exp $"


#define MTDSIZE	(sizeof (struct mtd_info) + sizeof (struct nand_chip))
#define MAX_PARTITIONS	16

#ifndef CONFIG_USE_PRELOADER_PARAMETERS 
extern nand_flash_info_t nandflash_info;
#else
/*define parameters struct for nand flash information */
extern parameter_to_bootloader_t kernel_soc_parameters;
#define plr_param_soc (kernel_soc_parameters.soc)
#define nandflash_info  (kernel_soc_parameters.soc.flash_info)
#endif

/* nand driver low-level functions */
static void rtk_nand_read_id(struct mtd_info *mtd, unsigned char id[5], int chip_sel);
static int rtk_read_oob(struct mtd_info *mtd, u16 chipnr, int page, int len, u_char *buf);
static int rtk_read_ecc_page(struct mtd_info *mtd, u16 chipnr, unsigned int chunk_id, 
			u_char *data, u_char *oob_buf);
static int rtk_write_ecc_page(struct mtd_info *mtd, u16 chipnr, unsigned int page, 
			const u_char *data, const u_char *oob_buf, int isBBT);
	
static int _nand_erase_block(struct mtd_info *mtd, u16 chipnr, int chunk_id);


/* Global Variables */
struct mtd_info *rtk_mtd; 
static DEFINE_MUTEX (sem);
static int page_size,chunk_size, oob_size, ppb;

static u32_t num_chunk_per_block;

#define ADDR_NOTALIGNED(byte, addr) ((addr & (byte-1)) != 0)  

#define flash_info_num_chunk_per_block	  \
		((nandflash_info.num_page_per_block)/(nandflash_info.page_per_chunk))



/* for fixed partition */
const char *ptypes[] = {"cmdlinepart", NULL};
//const char *ptypes[] = {"mtdparts=rtk_nand:640k@0(boot),6M@0x180000(linux),-(rootfs)", NULL};


//eric, use cmdlinepart now
static struct mtd_partition rtl8686_parts[] = {
	/*patch from linux 2.4*/
	{ name: "boot", 	offset: 0, size:  0xa0000, mask_flags: 0 }, 
	{ name: "linux",	offset: 0x180000, size: 0x600000 , mask_flags:0}, 
	{ name: "rootfs1",	offset: 0xa00000, size: 0xa00000 , mask_flags:0}, 
	{ name: "rootfs2",	offset: 0x1400000, size: 0xa00000 , mask_flags:0}, 
	{ name: "rootfs3",	offset: 0x1e00000, size: 0xa00000 , mask_flags:0}, 	
//	{ name: "rtk_rootfs",	offset: MTDPART_OFS_NXTBLK, size: MTDPART_SIZ_FULL, mask_flags:0}, 	
};



/*************************************************************************	
**  check_ready()
**	descriptions: check nand flash busy/ready bit
**	parameters: 
**	return: 
**  note: call by internal rtk flash driver 
*************************************************************************/
static void check_ready(void)
{
	while(1) {
		if(  ( rtk_readl(NACR) & 0x80000000) == 0x80000000 ) 
			break;
	}
}

/*************************************************************************	
**  rtk_nand_read_id()
**	descriptions: read nand flash id
**	parameters: 
**	return: id[5] , flash id
**  note: chip_sel not use
*************************************************************************/
static void rtk_nand_read_id(struct mtd_info *mtd, u_char id[5], int chip_sel)
{
	int id_chain;
	int nand_strap_pin;

	if(chip_sel>1)  //SD5 only supports chip0, chip1
		return;


	check_ready();
	rtk_writel( (rtk_readl(NACR) |ECC_enable|RBO|WBO), NACR);

	//rtk_writel(0x0, NACMR);	
	rtk_writel( ((1<<(Chip_Seletc_Base+chip_sel))|CMD_READ_ID) , NACMR);          //read ID command
	check_ready();

	rtk_writel( (0x0 |AD2EN|AD1EN|AD0EN) , NAADR);  //dummy address cycle
	check_ready();
	
	id_chain = rtk_readl(NADR);
	//printk("rtk_nand_read_id id_chain is %lx\n",id_chain);
	id[0] = id_chain & 0xff;
	id[1] = (id_chain >> 8) & 0xff;
	id[2] = (id_chain >> 16) & 0xff;
	id[3] = (id_chain >> 24) & 0xff;

	id_chain = rtk_readl(NADR);
	id[4] = id_chain & 0xff;

	printk("rtk_nand_read_id id is %x %x %x %x %x",id[0],id[1],id[2],id[3],id[4]);

	rtk_writel( 0x0, NACMR);
	rtk_writel( 0x0, NAADR);


}


/*************************************************************************	
**  rtk_PIO_read_is_allone()
**	descriptions: pio read nand data, check data is all one
**	parameters:  page index, offset
**	return:
**  note: internal usage for rtk driver
*************************************************************************/

int rtk_PIO_read_is_allone(int page, int offset)
{
	int i;
	unsigned int flash_addr1=0;
	unsigned int data_out;
	int rlen, real_page;
	unsigned int cmd;
	real_page = page;
	
/*
	if(offset==0 || offset==256){
		rlen = page_size/2;
		
		if(offset==0)
			cmd = CMD_PG_READ_A;
		else
			cmd = CMD_PG_READ_B;
		
	}else{
		rlen = oob_size;
		cmd = CMD_PG_READ_C;
	}
*/
	rlen = page_size + oob_size;
	cmd = CMD_PG_READ_A;

	rtk_writel(0xc00fffff, NACR);

	/* Command cycle 1*/
	rtk_writel((CECS0|cmd), NACMR);

	check_ready();

	flash_addr1 |= ((real_page & 0xffffff) << 8) ;

	/* Give address */
	rtk_writel( (AD2EN|AD1EN|AD0EN|flash_addr1), NAADR);

	check_ready();

	for(i=0; i<(rlen/4); i++){
		data_out = rtk_readl(NADR);
		//printf("[%3d] 0x%08X \n",i, data_out);
		if(data_out!=0xffffffff){
			printk("[%3d] 0x%08X \n",i, data_out);
			printk("%s: page %d offset %d i %d rlen %d\n",__FUNCTION__, page, offset, i, rlen);			
			return -1;
		}
	}

	check_ready();
	rtk_writel(0, NACMR);
	
	return 0;
	
}


/*************************************************************************	
**  rtk_check_allone_512()
**	descriptions: pio read nand data, check data is all one for 512 page size.
**	parameters:  page index,
**	return:
**  note: internal usage for rtk driver (RS code only)
*************************************************************************/

int rtk_check_allone_512(int page)
{
	int rc=0;

	//printf("[%s] page = %d\n",__func__,page);
	rc = rtk_PIO_read_is_allone(page,0);
	if(rc < 0)
		goto read_finish;
/*
	rc = rtk_PIO_read_is_allone(page,256);
	if(rc < 0)
		goto read_finish;

	rc = rtk_PIO_read_is_allone(page,512);
*/
read_finish:
	return rc;
}

/*************************************************************************	
**  rtk_check_allone()
**	descriptions: pio read nand data, check data is all one for large page size.
**	parameters:  page index,
**	return:
**  note: internal usage for rtk driver (RS code only)
*************************************************************************/

int rtk_check_allone(int page, int offset)
{
	unsigned int flash_addr1, flash_addr2;
	unsigned int data_out;
	int real_page, i, rlen;;

	real_page = page;

	/* rlen is equal to (512 + 16) */
	rlen = 528; 
	
	rtk_writel(0xc00fffff, NACR);

	/* Command cycle 1*/
	rtk_writel((CECS0|CMD_PG_READ_C1), NACMR);

	check_ready();

	flash_addr1 =  ((real_page & 0xff) << 16) | offset;
	flash_addr2 = (real_page >> 8) & 0xffffff;

	/* Give address */
	rtk_writel( (enNextAD|AD2EN|AD1EN|AD0EN|flash_addr1), NAADR);
	rtk_writel( (AD1EN|AD0EN|flash_addr2), NAADR);

	/* Command cycle 2*/
	rtk_writel((CECS0|CMD_PG_READ_C2), NACMR);

	check_ready();

	for(i=0; i<(rlen/4); i++){
		data_out = rtk_readl(NADR);	
		if( data_out != 0xffffffff)
			return -1;
	}

	check_ready();
	rtk_writel(0, NACMR);

	return 0;

}


/*function : rtk_read_oob */
static int rtk_read_oob (struct mtd_info *mtd, u16 chipnr, int chunk_id, int len, u_char *oob_buf)
{
	struct nand_chip *this = (struct nand_chip *) mtd->priv;

	return parameters._nand_read_chunk(this->g_databuf,oob_buf,chunk_id);
}

/* rtk_read_ecc_page*/
static int rtk_read_ecc_page (struct mtd_info *mtd, u16 chipnr, unsigned int chunk_id, 
			u_char *data_buf, u_char *oob_buf)
{

	return parameters._nand_read_chunk(data_buf,oob_buf,chunk_id);

}

/* function: write callback function */
int rtk_write_ecc_page (struct mtd_info *mtd, u16 chipnr, unsigned int chunk_id, 
			const u_char *data_buf, const u_char *oob_buf, int isBBT)	
{
	return parameters._nand_write_chunk(data_buf,oob_buf,chunk_id);	
}



/*************************************************************************	
**  rtk_nand_profile()
**	descriptions: rtk luna nand driver init function
**	parameters: 
**	return: 
**  note:  
*************************************************************************/
static int rtk_nand_profile (void)
{
	int maxchips = 4;	
	char *ptype;
	int pnum = 0;
	struct mtd_partition *mtd_parts;
	struct nand_chip *this = (struct nand_chip *)rtk_mtd->priv;
	
	if (rtk_nand_scan (rtk_mtd, maxchips) < 0 || rtk_mtd->size == 0){
		printk("%s: Error, cannot do nand_scan(on-board)\n", __FUNCTION__);
		return -ENODEV;
	}

	/*check page size(write size) is 512/2048/4096.. must 512byte align */
	if ( !(rtk_mtd->writesize&(0x200-1)) )
		;//rtk_writel( rtk_mtd->oobblock >> 9, REG_PAGE_LEN);
	else{ 
		printk("Error: pagesize is not 512Byte Multiple");
		return -1;
	}

	

#ifdef CONFIG_MTD_CMDLINE_PARTS
	/*partitions from cmdline */
	ptype = (char *)ptypes[0];
	pnum = parse_mtd_partitions (rtk_mtd, ptypes, &mtd_parts, 0);



	if (pnum <= 0) {	
		printk(KERN_NOTICE "RTK: using the whole nand as a partitoin\n");
		if(add_mtd_device(rtk_mtd)) {
			printk(KERN_WARNING "RTK: Failed to register new nand device\n");
			return -EAGAIN;
		}
	}else{
		printk(KERN_NOTICE "RTK: using dynamic nand partition\n");
		if (add_mtd_partitions (rtk_mtd, mtd_parts, pnum)) {	
			printk("%s: Error, cannot add %s device\n", 
					__FUNCTION__, rtk_mtd->name);
			rtk_mtd->size = 0;
			return -EAGAIN;
		}	
	}

#else
/* fixed partition ,modify rtl8686_parts table*/
	if(add_mtd_device(rtk_mtd)) {
		printk(KERN_WARNING "RTK: Failed to register new nand device\n");
		return -EAGAIN;
	}

	if (add_mtd_partitions (rtk_mtd, rtl8686_parts, ARRAY_SIZE(rtl8686_parts))) {	
			printk("%s: Error, cannot add %s device\n", 
					__FUNCTION__, rtk_mtd->name);
			rtk_mtd->size = 0;
			return -EAGAIN;
		}

	ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 0);

#endif
	return 0;
}

/*************************************************************************	
**  rtk_read_proc_nandinfo()
**	descriptions: proc read nand flash information
**	parameters: 
**	return: 
**  note:  
*************************************************************************/

int rtk_read_proc_nandinfo(struct seq_file *m, void *v)
{
	struct nand_chip *this = (struct nand_chip *) rtk_mtd->priv;

	seq_printf(m, "nand_size:%u\n", this->device_size);
	seq_printf(m, "chip_size:%u\n", this->chipsize);
	seq_printf(m, "block_size:%u\n", rtk_mtd->erasesize);
	seq_printf(m, "chunk_size:%u\n", rtk_mtd->writesize);
	seq_printf(m, "real page_size:%u\n", page_size);
	seq_printf(m, "oob_size:%u\n", rtk_mtd->oobsize);
	seq_printf(m, "ppb:%u\n", rtk_mtd->erasesize/rtk_mtd->writesize);

	return 0;
}

static int
rtk_open_proc_nandinfo(struct inode *inode, struct file *file)
{
	return single_open(file, rtk_read_proc_nandinfo, NULL);
}

/*************************************************************************	
**  display_version()
**	descriptions: display driver version
**	parameters: 
**	return: 
**  note:  
*************************************************************************/

static void display_version (void)
{
	const __u8 *revision;
	const __u8 *date;
	char *running = (__u8 *)VERSION;
	strsep(&running, " ");
	strsep(&running, " ");
	revision = strsep(&running, " ");
	date = strsep(&running, " ");
	printk(BANNER " Rev:%s (%s)\n", revision, date);
}

/*************************************************************************	
**  rtk_nand_init()
**	descriptions: rtk luna nand driver init function
**	parameters: 
**	return: 
**  note:  
*************************************************************************/

static const struct file_operations rtk_nand_proc_ops = {
	.open		= rtk_open_proc_nandinfo,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static int __init rtk_nand_init (void)
{
	struct nand_chip *this;
	int rc = 0;
	
	display_version();
	
	rtk_mtd = kmalloc (MTDSIZE, GFP_KERNEL); //mtd_info struct + nand_chip struct
	
	if ( !rtk_mtd ){
		printk("%s: Error, no enough memory for rtk_mtd\n",__FUNCTION__);
		rc = -ENOMEM;
		goto EXIT;
	}
	memset ( (char *)rtk_mtd, 0, MTDSIZE);
	rtk_mtd->priv = this = (struct nand_chip *)(rtk_mtd+1);

	rtk_writel(0xC00FFFFF, NACR);     //Enable ECC	
	rtk_writel(0x0000000F, NASR);     //clear NAND flash status register

	/*nand_chip struct assign call back function */	
	this->read_id			= rtk_nand_read_id;	/*read id function */
	this->read_ecc_page 	= rtk_read_ecc_page;
	this->read_oob 			= rtk_read_oob;
	this->write_ecc_page	= rtk_write_ecc_page;
	this->erase_block		= _nand_erase_block;
	this->sync			= NULL;

	/*init nand flash */
	if( rtk_nand_profile() < 0 ){
		rc = -1;
		goto EXIT;
	}
	
	chunk_size = rtk_mtd->writesize;
	page_size=nandflash_info.page_size;
	oob_size = rtk_mtd->oobsize;
	ppb = (rtk_mtd->erasesize)/(rtk_mtd->writesize);
	
	//create_proc_read_entry("nandinfo", 0, NULL, rtk_read_proc_nandinfo, NULL);
	proc_create("nandinfo", 0, NULL, &rtk_nand_proc_ops);

EXIT:
	if (rc < 0) {
		if (rtk_mtd){
			del_mtd_partitions (rtk_mtd);
			if (this->g_databuf)
				kfree(this->g_databuf);
			if(this->g_oobbuf)
				kfree(this->g_oobbuf);

			kfree(rtk_mtd);
		}
		remove_proc_entry("nandinfo", NULL);
	}else
		printk(KERN_INFO "Realtek Nand Flash Driver is successfully installing.\n");
	
	return rc;
}

/**	
**  nand_check_eccStatus()
**	descriptions: check ecc counter,status.
**	parameters:  none
**	return: 0: ecc ok, 1~N ,ecc can fix it, return ecc counter,  -1: ecc can't fix it.
**  note: 
**/
int nand_check_eccStatus(void)
{
	int error_count,status;
	int rc=0;
	
	status = rtk_readl(NASR);
	
	if( (status & NDRS)== NDRS){	 //read status
		if( status & NRER) { //ecc result = 1 , ecc read fail.
	 		error_count = (status & 0xf0) >> 4;
	 		/*ecc correction counter must change */
	 		if(error_count <=4 && error_count > 0 ) { //some bit fail, but ecc can fix it.
				//printk("[%s] R: Correctable HW ECC Error at page=%u, status=0x%08X\n\r", __FUNCTION__, page,status);
				status &= 0x0f; //clear NECN
				rtk_writel(status, NASR);
				rc=error_count;
			}else{ 
				//counter = 0 and ecc fail,  it mean ecc can' fix it
				rc = -1;					
			}
		}
	}else if( (status & NDWS)== NDWS){ //write status
			if( status & NWER) {
//				printk("Un-Correctable HW ECC Error in Write \n");
				rc=-1;		
			}
	}else {
		printk("!!!! nand_check_eccStatus ERROR !!! \n");
	}
	status |= 0x0f; //clear status.
	rtk_writel(status, NASR);	
	return rc;		
	
}

/* Read Toshiba BENAND ECC status */
/**	return:  -1 is fail , 0 is success */

int nand_check_toshiba_eccStatus(void){

	int ecc_status;
	
	rtk_writel((CECS0|CMD_TOSHIBA_BENAND_ECCSTATUS),NACMR);//read ecc status
	check_ready();

	ecc_status=rtk_readl(NADR);
	
	//printf("status %x\n",ecc_status); //read 4byte data

	/*check ecc status have bit can't correct */

	if(( ecc_status & 0xff == 0x3f) || 
	    (((ecc_status >> 8) & 0xff)==0x2f) || 
	    (((ecc_status >> 16) & 0xff)==0x1f) || 
	    (((ecc_status >> 24) & 0xff)==0x0f)){
		//printf(" ecc error happen\n");		
		return -1;
	}
	return 0;
}


/**	
**  _nand_read_noecc_chunk()
**	descriptions: DAM read disable HW ECC for toshiba be-nandflash
**	parameters: chunk_buf, data buffer point 
 				spare , oob buffer point
	 			chunk_id, chunk number (will Calculate to real page number)
**	return:  -1 is fail, 0~N : ecc counter
**  note:  one chunk = 2k data.
**/

int
_nand_read_noecc_chunk(u8_t *chunk_buf, spare_u *spare, u32_t chunk_id) {
	int real_page = chunk_id*nandflash_info.page_per_chunk; //get real_page number
	int dma_counter = 4; //always 512byte * 4
	int dram_sa, oob_sa;
	int page_shift;
	int page_num[3];
	unsigned long flash_addr_t=0;
	unsigned long flash_addr2_t=0;
	int buf_pos=0;
	int return_value=0;
	int ecc_count;
	int i;
	int block = real_page/nandflash_info.num_page_per_block;
	u8_t tempvalue;
	u8_t *oob_area;
	u8_t temp_buf;
	u8_t *data_area=NULL;
	u8_t data_area0[512+16+CACHELINE_SIZE+CACHELINE_SIZE-4]; //data,oob point must 32 cache aligment
	unsigned long flags;
	
		
	if((chunk_buf == NULL)||(spare == NULL)){
		printk("chunk_buf/spare point is null\n");
		return -1;
	}
	
	memset(data_area0, 0xff, 512+16+CACHELINE_SIZE+CACHELINE_SIZE-4);
	data_area = (u8_t*) ((u32_t)(data_area0 + CACHELINE_SIZE-4) & 0xFFFFFFF0);
	oob_area=(u8_t*) data_area+512;
		
	oob_sa = ((uint32_t)oob_area) & (~M_mask);
	dram_sa = ((u32_t)data_area) & (~M_mask);
	
	rtk_writel(0x800FFFFF, NACR);	  //Disable CPU ECC
	rtk_writel(0x0000000F, NASR);	  //clear NAND flash status register
	
	for(page_shift=0;page_shift<3; page_shift++) {
		page_num[page_shift] = ((real_page>>(8*page_shift)) & 0xff);
		if(nandflash_info.page_size==2048){
			flash_addr_t |= (page_num[page_shift] << (12+8*page_shift));
	  }else if(nandflash_info.page_size==4096){
		  flash_addr_t |= (page_num[page_shift] << (13+8*page_shift));
	  }else if(nandflash_info.page_size==512){ //512byte
			flash_addr_t |= (page_num[page_shift] << (9+8*page_shift));
		}
	}
	
	  // If the nand flash size is larger than 2G bytes, fill the other address for DMA
	  flash_addr2_t= (real_page >> 20);
	

  /* DMA start read */
	
	while(dma_counter>0){
		
		//set DMA RAM DATA start address
		rtk_writel(dram_sa, NADRSAR);
		//set DMA RAM oob start address , always use oob_sa buffer
		rtk_writel(oob_sa, NADTSAR);
		
		//set DMA flash start address,
		rtk_writel( flash_addr_t, NADFSAR); //write flash address 
		rtk_writel( flash_addr2_t, NADFSAR2);

		dma_cache_wback_inv((u32_t *)data_area,528);
		local_irq_save(flags);			
		//DMA read command
		rtk_writel( ((~TAG_DIS)&(DESC0|DMARE|LBC_64)),NADCRR);
		check_ready();
		//check status register 
		local_irq_restore(flags);
		//copy data
		memcpy(chunk_buf+(buf_pos*512), data_area, 512);	  
		//copy oob
		memcpy(spare->u8_oob+(buf_pos*6), oob_area, 6);	
		flash_addr_t += 528;//move next flash addr add (512+6+10)bytes
		dma_counter--;
		buf_pos++; 
				
	}

	
	//check status register
	return_value=nand_check_toshiba_eccStatus();

	if(block>0){
		//change bbi,data
		temp_buf=chunk_buf[nandflash_info.bbi_dma_offset];		
		chunk_buf[nandflash_info.bbi_dma_offset] = spare->u8_oob[nandflash_info.bbi_swap_offset];
		spare->u8_oob[nandflash_info.bbi_swap_offset]=temp_buf;
	}
	  
	return return_value;


}


/**
**  _nand_PIO_write_noecc_chunk()
**	descriptions: PIO write ,flash layout the same dma write
**	parameters:  chunk id  (must alignment of real_page/block)
**	return:  0:succes,-1 is fail
**  note: this function not check bad block table.
**/


int
_nand_PIO_write_noecc_chunk(u8_t *chunk_buf, spare_u *spare, u32_t chunk_id) {
	int real_page = chunk_id*nandflash_info.page_per_chunk; //get real_page number
	int dma_counter = 4; //always 512byte * 4
	int dram_sa, oob_sa;
	int page_shift;
	int page_num[3];
	unsigned long flash_addr_t=0;
	unsigned long flash_addr2_t=0;
	int buf_pos=0;
	int return_value=0;
	int ecc_count=0;
	int i;
	unsigned long flags;
	int block = real_page/nandflash_info.num_page_per_block;

	u8_t *oob_area;
	u8_t *data_area, data_area0[2112];  //2048+64 , currently driver only support page + oob = 2112

	//printk("_nand_PIO_write_noecc_chunk chunk_id %x\n",chunk_id);
	memset(data_area0, 0xff, sizeof(data_area0));

	data_area=(u8_t*) (u32_t) data_area0;
	oob_area=(u8_t*) data_area+512;
	
	//printk("_nand_write_chunk : spare->u8_oob 0x%p\n",spare->u8_oob );

	if(block>0){
		//swap bbi and data
		spare->u8_oob[nandflash_info.bbi_swap_offset]=chunk_buf[nandflash_info.bbi_dma_offset];
		chunk_buf[nandflash_info.bbi_dma_offset]=0xff;
	}

	/* dma move  4*528byte */
	
	while(dma_counter>0){
		//copy oob to buffer
		
		memcpy(data_area+(buf_pos*528), chunk_buf+(buf_pos*512), 512);
		oob_area=(u8_t*) data_area+(buf_pos*528)+512;
		memcpy(oob_area, spare->u8_oob+(buf_pos*6), 6);
		dma_counter--;
		buf_pos++;
	}


	if(block>0){
		//swap bbi and data again. (recovery data)
		chunk_buf[nandflash_info.bbi_dma_offset]= spare->u8_oob[nandflash_info.bbi_swap_offset];
		//eric mark not need
	//	dma_cache_wback_inv((u32_t *)chunk_buf,2048);
	}
	

	rtk_PIO_write(chunk_id,0,2112,data_area);

	return return_value;
}








/**	
**  _nand_read_chunk()
**	descriptions: DAM read one ecc chunk  
**	parameters: chunk_buf, data buffer point 
 								spare , oob buffer point
	 							chunk_id, chunk number (will Calculate to real page number)
**	return:  -1 is fail, 0~N : ecc counter
**  note:  one chunk = 2k data.
**/
// parameters._nand_read_chunk

int 
_nand_read_chunk(u8_t *chunk_buf, spare_u *spare, u32_t chunk_id) {
  int real_page = chunk_id*nandflash_info.page_per_chunk; //get real_page number
	int dma_counter = 4; //always 512byte * 4
	int dram_sa, oob_sa;
	int page_shift;
	int page_num[3];
	unsigned long flash_addr_t=0;
	unsigned long flash_addr2_t=0;
	int buf_pos=0;
	int return_value=0;
	int ecc_count;
	int i;
	int block = real_page/nandflash_info.num_page_per_block;
	u8_t tempvalue;
	u8_t *oob_area;
	u8_t temp_buf;
	u8_t *data_area=NULL;
	u8_t data_area0[512+16+CACHELINE_SIZE+CACHELINE_SIZE-4]; //data,oob point must 32 cache aligment
	unsigned long flags;

	
	if((chunk_buf == NULL)||(spare == NULL)){
		printk("chunk_buf/spare point is null\n");
		return -1;
	}

	memset(data_area0, 0xff, 512+16+CACHELINE_SIZE+CACHELINE_SIZE-4);
	data_area = (u8_t*) ((u32_t)(data_area0 + CACHELINE_SIZE-4) & 0xFFFFFFF0);
	oob_area=(u8_t*) data_area+512;
		
	oob_sa = ((uint32_t)oob_area) & (~M_mask);
	dram_sa = ((u32_t)data_area) & (~M_mask);



	rtk_writel(0xC00FFFFF, NACR);     //Enable ECC	
	rtk_writel(0x0000000F, NASR);	  //clear NAND flash status register

	for(page_shift=0;page_shift<3; page_shift++) {
		page_num[page_shift] = ((real_page>>(8*page_shift)) & 0xff);
		if(nandflash_info.page_size==2048){
			flash_addr_t |= (page_num[page_shift] << (12+8*page_shift));
	  }else if(nandflash_info.page_size==4096){
		  flash_addr_t |= (page_num[page_shift] << (13+8*page_shift));
	  }else if(nandflash_info.page_size==512){ //512byte
			flash_addr_t |= (page_num[page_shift] << (9+8*page_shift));
		}
	}

  // If the nand flash size is larger than 2G bytes, fill the other address for DMA
  flash_addr2_t= (real_page >> 20);

	

  /* DMA start read */

	while(dma_counter>0){
  	
		//set DMA RAM DATA start address
  		rtk_writel(dram_sa, NADRSAR);
		//set DMA RAM oob start address , always use oob_sa buffer
		rtk_writel(oob_sa, NADTSAR);
		
		//set DMA flash start address,
 		rtk_writel( flash_addr_t, NADFSAR); //write flash address 
#if 1 //BCH
		rtk_writel( flash_addr2_t, NADFSAR2);
#endif

		dma_cache_wback_inv((u32_t *)data_area,528);
		local_irq_save(flags);			
		//DMA read command
	    rtk_writel( ((~TAG_DIS)&(DESC0|DMARE|LBC_64)),NADCRR);
		check_ready();
	  	//check status register 
	local_irq_restore(flags);
	  	ecc_count=nand_check_eccStatus();


		if((return_value!=-1) && (ecc_count != -1))
		{
			return_value= (ecc_count>return_value)?ecc_count:return_value;
		}else{
			return_value=-1;
		}


			//copy data
		memcpy(chunk_buf+(buf_pos*512), data_area, 512);	  
		//copy oob
		memcpy(spare->u8_oob+(buf_pos*6), oob_area, 6);
			

		flash_addr_t += 528;//move next flash addr add (512+6+10)bytes
		dma_counter--;
		buf_pos++; 
			
	}


	if(block>0){
		//change bbi,data
		temp_buf=chunk_buf[nandflash_info.bbi_dma_offset];		
  		chunk_buf[nandflash_info.bbi_dma_offset] = spare->u8_oob[nandflash_info.bbi_swap_offset];
		spare->u8_oob[nandflash_info.bbi_swap_offset]=temp_buf;
  	}
//eric mark, not need flush cache
//	dma_cache_wback_inv((u32_t *)chunk_buf,2048);	
//	dma_cache_wback_inv((u32_t *)spare->u8_oob,64);


#if 0	 //only for test chip
	  if(return_value==-1){ //ecc fail ,test chip must check data is all 0xff
			return_value=0; 		
			for(i=0; i<(24); i++){
				if( spare->u8_oob[i] != 0xff){
					printk("ecc fail in _nand_read_chunk\n");
		//		  	if(data_area0!=NULL)
			//			kfree(data_area0);
					return_value= -1;
				  }
			}
	  }
#endif  

//	if(data_area0!=NULL)
	//	kfree(data_area0);
  
  return return_value;


}


/**	
**  _nand_read_bbi()
**	descriptions: read flash bbi byte and return it
**	parameters:  byte: bbi offset byte (should be nandflash_info.bbi_offset) , chunk id: chunk number
**	return:  1 is badblock,  0 is ok block.
**  Note: only for page size 2k , 
**/
u8_t
_nand_read_bbi(u32_t bytes, u32_t chunk_id ) {
	int real_page;
	unsigned int flash_addr1, flash_addr2;
	unsigned int data_out;
	real_page = chunk_id*nandflash_info.page_per_chunk;  //get real_page number

	rtk_writel(0xc00fffff, NACR);
	


	if(nandflash_info.page_size==512){
		flash_addr1 = (real_page << 8) & 0xffffff;
		flash_addr2 = (real_page >> 16) & 0xffffff;

	}else{
		flash_addr1 =  ((real_page & 0xff) << 16) | bytes;
		flash_addr2 = (real_page >> 8) & 0xffffff;
	}


	

	/* Command cycle 1*/
	if(nandflash_info.page_size!=512){	  
	rtk_writel((CECS0|CMD_PG_READ_C1), NACMR);

	}else{
		/*bbi should in spare-area (oob) 512+16 */
		bytes-=512; 		
		flash_addr1|=bytes; 
		/* Command cycle 1*/
		rtk_writel((CECS0|CMD_PG_READ_512_R3), NACMR); // CMD 50h	
	}
	check_ready();
	

	

	//use pio read.
	//write address to flash control	
	switch(nandflash_info.addr_cycles){
		case 3:
			rtk_writel( (AD2EN|AD1EN|AD0EN|flash_addr1), NAADR);			
			break;
		case 4:
			/* set address 1 */
	rtk_writel( (enNextAD|AD2EN|AD1EN|AD0EN|flash_addr1), NAADR);
			check_ready();				
		rtk_writel( (AD0EN|flash_addr2), NAADR);
			break;
		case 5:
			rtk_writel( (enNextAD|AD2EN|AD1EN|AD0EN|flash_addr1), NAADR);
			check_ready();				
			rtk_writel( (AD1EN|AD0EN|flash_addr2), NAADR);
			break;
	}
	check_ready();

	/* Command cycle 2*/
	if(nandflash_info.page_size!=512){ //512 page size not need 'end' command 
		/* Command cycle 2*/
	rtk_writel((CECS0|CMD_PG_READ_C2), NACMR);
	check_ready();
	}

	data_out = rtk_readl(NADR); //read 4 byte from NADR.
	
	if(((data_out >> 24) & 0xff) !=0xff){
		return 1;		//bad
	}
	
	return 0; //good

}


/*************************************************************************	
**  _nand_erase_block()
**	descriptions: erase nand flash block
**	parameters: chunk id  (must alignment of real_page/block)
			 	mtd/chipnr un-used
**	return: 0:succes,-1 is fail
**  note: this function not check bad block table.
    
*************************************************************************/

static int
_nand_erase_block(struct mtd_info *mtd, u16 chipnr,int chunk_id) {

	int addr_cycle[5],page_shift;
	int real_page = chunk_id*nandflash_info.page_per_chunk; //get real_page number

 	if (down_interruptible (&sem)) {
		printk("%s : user breaking\n",__FUNCTION__);
		return -ERESTARTSYS;
	}
 
	if ( real_page & (nandflash_info.num_page_per_block-1) ){
		printk("%s: (page)chunk_id %d is not block alignment !!\n", __FUNCTION__, real_page);
		up (&sem);
		return -1;
	}

//	rtk_writel( (rtk_readl(NACR) |ECC_enable), NACR); //erase not need set this reg
	rtk_writel((NWER|NRER|NDRS|NDWS), NASR);
	rtk_writel(0x0, NACMR);

	rtk_writel((CECS0|CMD_BLK_ERASE_C1),NACMR); //Command register , write erase command (1 cycle)
	check_ready();

	if(nandflash_info.addr_cycles!=3){
	for(page_shift=0; page_shift<3; page_shift++){
		addr_cycle[page_shift] = (real_page>>(8*page_shift)) & 0xff;
	}

		rtk_writel( ((~enNextAD) & (AD2EN|AD1EN|AD0EN|(addr_cycle[0]<<CE_ADDR0) |(addr_cycle[1]<<CE_ADDR1)|(addr_cycle[2]<<CE_ADDR2))),NAADR); //NAND Flash Address Register1
		
	}else{
		addr_cycle[0] = 0;
		for(page_shift=0; page_shift<4; page_shift++){
			addr_cycle[page_shift+1] = (real_page>>(8*page_shift)) & 0xff;
		}		
	rtk_writel( ((~enNextAD) & AD2EN|AD1EN|AD0EN|
				(addr_cycle[1]<<CE_ADDR0) |(addr_cycle[2]<<CE_ADDR1)|(addr_cycle[3]<<CE_ADDR2)),NAADR);
	}

			
	check_ready();

	rtk_writel((CECS0|CMD_BLK_ERASE_C2),NACMR); //write erase command cycle 2
	check_ready();

	rtk_writel((CECS0|CMD_BLK_ERASE_C3),NACMR);//read status
	check_ready();

	
	if ( rtk_readl(NADR) & 0x01 ){
		printk("[%s] erase block is not completed at block %d\n", __FUNCTION__, real_page/nandflash_info.num_page_per_block);
		up (&sem);			
		return -1;
	}else{
		up (&sem);		
		return 0;
	}
 
}


/*************************************************************************	
**  _nand_write_chunk()
**	descriptions: write data to nand flash
**	parameters: chunk id  (must alignment of real_page/block)			 
**	return: 0:succes,-1 is fail
**  note: this function not check bad block table.
    
*************************************************************************/


int
_nand_write_chunk(u8_t *chunk_buf, spare_u *spare, u32_t chunk_id) {
	int real_page = chunk_id*nandflash_info.page_per_chunk; //get real_page number
	int dma_counter = 4; //always 512byte * 4
	int dram_sa, oob_sa;
	int page_shift;
	int page_num[3];
	unsigned long flash_addr_t=0;
	unsigned long flash_addr2_t=0;
	int buf_pos=0;
	int return_value=0;
	int ecc_count=0;
	int i;
	unsigned long flags;
	int block = real_page/nandflash_info.num_page_per_block;

	u8_t *oob_area;
	u8_t *data_area, data_area0[512+16+CACHELINE_SIZE+CACHELINE_SIZE-4]; //data,oob point must 32 cache aligment

	memset(data_area0, 0xff, 512+16+CACHELINE_SIZE+CACHELINE_SIZE-4);


	data_area = (u8_t*) ((u32_t)(data_area0 + 12) & 0xFFFFFFF0);
	oob_area=(u8_t*) data_area+512;
	
	//printk("_nand_write_chunk : spare->u8_oob 0x%p\n",spare->u8_oob );

	if(block>0){
		//swap bbi and data
		spare->u8_oob[nandflash_info.bbi_swap_offset]=chunk_buf[nandflash_info.bbi_dma_offset];
 	 	chunk_buf[nandflash_info.bbi_dma_offset]=0xff;
  	}
//	dma_cache_wback_inv((u32_t *)chunk_buf,2048);

	//set DMA flash start address
	for(page_shift=0;page_shift<3; page_shift++) {
	    page_num[page_shift] = ((real_page>>(8*page_shift)) & 0xff);
		if(nandflash_info.page_size==2048){		
			flash_addr_t |= (page_num[page_shift] << (12+8*page_shift));
		}else if(nandflash_info.page_size==4096){
			flash_addr_t |= (page_num[page_shift] << (13+8*page_shift));		
		}else if(nandflash_info.page_size==512){/* 512 */
			flash_addr_t |= (page_num[page_shift] << (9+8*page_shift));
		}
	}

#if 1 //BCH
		// If the nand flash size is larger than 2G bytes, fill the other address for DMA
		flash_addr2_t= (real_page >> 20);
#endif
	
	rtk_writel(0xC00FFFFF, NACR);     //Enable ECC	
	rtk_writel(0x0000000F, NASR);	  //clear NAND flash status register
	
 	oob_sa =  ( (u32_t)(oob_area ) & (~M_mask));
	dram_sa = ((u32_t)data_area) & (~M_mask);

	/* dma move  4*528byte */

  	while(dma_counter>0){

		//copy oob to buffer
		memcpy(oob_area, spare->u8_oob+(buf_pos*6), 6);
		memcpy(data_area, chunk_buf+(buf_pos*512), 512);
		dma_cache_wback_inv((u32_t *)data_area,528);//512+16
		rtk_writel( rtk_readl(NACR) & (~RBO) & (~WBO) , NACR);
		//write data/oob address
		rtk_writel(dram_sa, NADRSAR);
		rtk_writel( oob_sa, NADTSAR);
		rtk_writel( flash_addr_t, NADFSAR);
#if 1//BCH		
		rtk_writel( flash_addr2_t, NADFSAR2);		
#endif
		//dma write cmd
	local_irq_save(flags);			
		rtk_writel( (DESC0|DMAWE|LBC_64 & (~TAG_DIS)),NADCRR);

		check_ready();
			//check status register 
		local_irq_restore(flags);
#if 0 //eric not need check. for write always ok...
		ecc_count=nand_check_eccStatus();
	  //Record ecc counter ,return the max number
		if((return_value!=-1) && (ecc_count != -1))
		{
			return_value= (ecc_count>return_value)?ecc_count:return_value;
		}else{
			return_value=-1;
			//printk("\r\n _nand_write_chunk return -1\n");
		}
#endif		
		rtk_writel(0xF, NASR);	


		flash_addr_t += (528); //512+16 one unit

		dma_counter--;
		buf_pos++;
	}
	
	if(block>0){
		//swap bbi and data again. (recovery data)
		chunk_buf[nandflash_info.bbi_dma_offset]= spare->u8_oob[nandflash_info.bbi_swap_offset];
		//eric mark not need
	//	dma_cache_wback_inv((u32_t *)chunk_buf,2048);
	}
	

	return return_value;
}




/**
**  create_bbt()
**	descriptions: create bbt table 
**	parameters:  dma_buf (temp buffer)
**	return: 
**  note: pio write is by pass hardware ecc.
**/


inline static u8_t*
pbldr_spare_bbi(spare_u *spare) {
	return &(spare->u8_oob[nandflash_info.bbi_swap_offset]);
}

#ifndef CONFIG_USE_PRELOADER_PARAMETERS

void
create_bbt(nand_dma_buf_t *dma_buf,u32_t *bbt_table) {
    u32_t b, ecc;
    u8_t bbi;

	dbg_printf("create bbt , for loop %d num_chunk_per_block %d\n",nandflash_info.num_block,num_chunk_per_block);
	
	memset(bbt_table, 0x0, NUM_WORD(MAX_BLOCKS));
	
    for (b=1;b<nandflash_info.num_block;++b) {
		
		bbi=_nand_read_bbi(nandflash_info.bbi_raw_offset,b*num_chunk_per_block);
        if (bbi==0) continue;
		dbg_printf("1 bad block[%d], page %d , bbi %x\n",b,b*num_chunk_per_block,bbi);
		if ((ecc=parameters._nand_read_chunk(dma_buf->chunk_buf, &dma_buf->spare, b*num_chunk_per_block))<0) {
		
			
			dbg_printf("read fail, set block %d is bad \n",b);
            _set_flags(bbt_table, b);
            continue;
        }
        u8_t oob_bbi=*pbldr_spare_bbi(&(dma_buf->spare));
		dbg_printf("oob_bbi is %x\n",oob_bbi);
        if (oob_bbi==0xFF) continue;
        if ((oob_bbi==0x00) && (dma_buf->spare.oob.signature==OOB_SIG_RESERVED)) continue;
 
        // mark bad block
        dbg_printf("2 bad block %d b is bad \n",b);
        _set_flags(bbt_table, b);
    }
//    parameters.bbt_valid=1;
	dbg_printf("finish create_bbt\n");
	
}

int nand_default(void){

	int nand_strap_pin;
	
	//read from strap pin
	nand_strap_pin= rtk_readl(NACFR);

//READ page size strap ping
 switch( (nand_strap_pin & 0xC0000000) >>30){
 		case 0:
 					nandflash_info.page_size = 512;
 					break;
 		case 1:
 					nandflash_info.page_size = 2048;
 					break;
 		case 2:
 			 	  nandflash_info.page_size = 4096;
 					break;		
  	case 3:
 			 	  nandflash_info.page_size = 8192;
 					break;			 	
	}
	//read address cycle pin
	nandflash_info.addr_cycles = ((nand_strap_pin & 0x30000000) >>28)+3;  //3,4,5 address cycle	


	/*this hard-copy ,page 2k , block number 1024, total 128MB */
	
	nandflash_info.num_block  = 1024;
	//page number of one block
	nandflash_info.num_page_per_block = 64;
	nandflash_info.page_per_chunk=1;
	nandflash_info.bbi_dma_offset=2000; 
	//for 2048+64 pagesize, if spare_area byte[0] is bbi, in dma data_buffer is byte 2000, because must ignore 16byte oob *3, 
	// oob buffer not inside in data buffer.
	nandflash_info.bbi_raw_offset=2048; //bbi in nand flash raw address, for pio read 
	
	nandflash_info.bbi_swap_offset=23; // 512+16 nand control read, use 4th 512's oob byte[5] for data swap
	                               // after dma move to oob buffer, data byte is in oob_buf[23]  (6+6+6+6) (no ecc byte)


}

#endif


int
_nand_init(void){

	/* initialize hardware */
 
	rtk_writel(0x0000000F, NASR);     //clear NAND flash status register
	rtk_writel( (rtk_readl(NACR) |ECC_enable|RBO|WBO), NACR); //Enable ECC	

#if 1

//printk("parameters at 0x%p\n",parameters);
//printk("parameters.read at 0x%p\n",parameters._nand_read_chunk);
//printk("parameters.write at 0x%p\n",parameters._nand_write_chunk);
//printk("parameters.bbt at 0x%p\n",parameters.bbt);
//printk("uboot- read nand flash info from SRAM\n");

printk("flash_info list\n");
printk("flash_info.num_block : %d\n",nandflash_info.num_block);
printk("flash_info.num_page_per_block : %d\n",nandflash_info.num_page_per_block);
printk("flash_info.page_per_chunk : %d\n",nandflash_info.page_per_chunk);
printk("flash_info.bbi_dma_offset : %d\n",nandflash_info.bbi_dma_offset);
printk("flash_info.bbi_raw_offset : %d\n",nandflash_info.bbi_raw_offset);
printk("flash_info.bbi_swap_offset : %d\n",nandflash_info.bbi_swap_offset);
printk("flash_info.page_size : %d\n",nandflash_info.page_size);
printk("flash_info.addr_cycles : %d\n",nandflash_info.addr_cycles);
printk("pblr_start_block : %d\n",nandflash_info.pblr_start_block);
printk("num_pblr_block : %d\n",nandflash_info.num_pblr_block);
#ifdef CONFIG_USE_PRELOADER_PARAMETERS
//	printk("parameters.curr_ver is %x\n",preloader_parameters.curr_ver);
//	printk("parameters.plr_num_chunk is %d\n",preloader_parameters.plr_num_chunk);
//	printk("parameters.blr_num_chunk is %d\n",preloader_parameters.blr_num_chunk);
//	printk("parameters.end_pblr_block is %d\n",preloader_parameters.end_pblr_block);
#endif

#endif

	chunk_size=nandflash_info.page_size*nandflash_info.page_per_chunk;
	num_chunk_per_block=flash_info_num_chunk_per_block;
	printk("chunk size is %d\n",chunk_size);
	printk("flash_info_num_chunk_per_block is %d\n",flash_info_num_chunk_per_block);
	
	return 0;
}


void __exit rtk_nand_exit (void)
{
	if (rtk_mtd){
		del_mtd_partitions (rtk_mtd);
		struct nand_chip *this = (struct nand_chip *)rtk_mtd->priv;
		if (this->g_databuf)
			kfree(this->g_databuf);
		if(this->g_oobbuf)
			kfree(this->g_oobbuf);
		kfree(rtk_mtd);
	}
	remove_proc_entry("nandinfo", NULL);
}

module_init(rtk_nand_init);
module_exit(rtk_nand_exit);
MODULE_AUTHOR(" Eric<ericchung@realtek.com>");
MODULE_DESCRIPTION("Realtek Luna NAND Flash Controller Driver");