#include "nandflash.h"
#include "jz4740.h"
#include "usb_boot.h"
#include "hand.h"
#define __nand_enable() (REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1)
#define __nand_disable() (REG_EMC_NFCSR &= ~(EMC_NFCSR_NFCE1))
#define __nand_ecc_rs_encoding() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST | EMC_NFECR_RS | EMC_NFECR_RS_ENCODING)
#define __nand_ecc_rs_decoding() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST | EMC_NFECR_RS | EMC_NFECR_RS_DECODING)
#define __nand_ecc_disable() (REG_EMC_NFECR &= ~EMC_NFECR_ECCE)
#define __nand_ecc_encode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_ENCF))
#define __nand_ecc_decode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_DECF))
#define __nand_ready() ((REG_GPIO_PXPIN(2) & 0x40000000) ? 1 : 0)
#define __nand_ecc() (REG_EMC_NFECC & 0x00ffffff)
#define __nand_cmd(n) (REG8(cmdport) = (n))
#define __nand_addr(n) (REG8(addrport) = (n))
#define __nand_data8() REG8(dataport)
#define __nand_data16() REG16(dataport)
#define CMD_READA 0x00
#define CMD_READB 0x01
#define CMD_READC 0x50
#define CMD_ERASE_SETUP 0x60
#define CMD_ERASE 0xD0
#define CMD_READ_STATUS 0x70
#define CMD_CONFIRM 0x30
#define CMD_SEQIN 0x80
#define CMD_PGPROG 0x10
#define CMD_READID 0x90
#define OOB_BAD_OFF 0x00
#define OOB_ECC_OFF 0x04
#define OP_ERASE 0
#define OP_WRITE 1
#define OP_READ 2
#define ECC_BLOCK 512
#define ECC_POS 6
#define PAR_SIZE 9
#define ECC_SIZE 36
static volatile unsigned char *gpio_base = (volatile unsigned char *)0xb0010000;
static volatile unsigned char *emc_base = (volatile unsigned char *)0xb3010000;
static volatile unsigned char *addrport = (volatile unsigned char *)0xb8010000;
static volatile unsigned char *dataport = (volatile unsigned char *)0xb8000000;
static volatile unsigned char *cmdport = (volatile unsigned char *)0xb8008000;
static int bus = 8, row = 2, pagesize = 2048, oobsize = 64, ppb = 128;
static int bad_block_pos,bad_block_page,force_erase,ecc_pos,wp_pin;
extern hand_t Hand;
//static u8 data_buf[2048] = {0};
static u8 oob_buf[256] = {0};
extern u16 handshake_PKT[4];
#define dprintf(x) serial_puts(x)
static unsigned int EMC_CSN[4]=
{
0xb8000000,
0xb4000000,
0xa8000000,
0xa4000000
};
static inline void __nand_sync(void)
{
unsigned int timeout = 100000;
while ((REG_GPIO_PXPIN(2) & 0x40000000) && timeout--);
while (!(REG_GPIO_PXPIN(2) & 0x40000000));
}
static void select_chip(int block)
{
int t;
if (!Hand.nand_bpc) return;
t = (block / Hand.nand_bpc) % 4;
addrport = (volatile unsigned char *)(EMC_CSN[t] + 0x10000);
dataport = (volatile unsigned char *)EMC_CSN[t];
cmdport = (volatile unsigned char *)(EMC_CSN[t] + 0x8000);
}
static int read_oob(void *buf, u32 size, u32 pg);
static int nand_data_write8(char *buf, int count);
static int nand_data_write16(char *buf, int count);
static int nand_data_read8(char *buf, int count);
static int nand_data_read16(char *buf, int count);
static int (*write_proc)(char *, int) = NULL;
static int (*read_proc)(char *, int) = NULL;
static nand_init_gpio(void)
{
//modify this fun to a specifical borad
//this fun init those gpio use by all flash chip
//select the gpio function related to flash chip
__gpio_as_nand();
}
inline void nand_enable_4740(unsigned int csn)
{
//modify this fun to a specifical borad
//this fun to enable the chip select pin csn
//the choosn chip can work after this fun
//dprintf("\n Enable chip select :%d",csn);
__nand_enable();
}
inline void nand_disable_4740(unsigned int csn)
{
//modify this fun to a specifical borad
//this fun to enable the chip select pin csn
//the choosn chip can not work after this fun
//dprintf("\n Disable chip select :%d",csn);
__nand_disable();
}
unsigned int nand_query_4740(u8 *id)
{
__nand_sync();
__nand_cmd(CMD_READID);
__nand_addr(0);
id[0] = __nand_data8(); //VID
id[1] = __nand_data8(); //PID
id[2] = __nand_data8(); //CHIP ID
id[3] = __nand_data8(); //PAGE ID
id[4] = __nand_data8(); //PLANE ID
return 0;
}
int nand_init_4740(int bus_width, int row_cycle, int page_size, int page_per_block,
int bbpage,int bbpos,int force,int ep)
{
bus = bus_width;
row = row_cycle;
pagesize = page_size;
oobsize = pagesize / 32;
ppb = page_per_block;
bad_block_pos = bbpos;
bad_block_page = bbpage;
force_erase = force;
ecc_pos = ep;
wp_pin = Hand.nand_wppin;
// nand_enable(0);
/* Initialize NAND Flash Pins */
if (wp_pin)
{
__gpio_as_output(wp_pin);
__gpio_disable_pull(wp_pin);
}
nand_init_gpio();
select_chip(0);
REG_EMC_SMCR1 = 0x0fff7700; //slow speed
// REG_EMC_SMCR1 = 0x04444400; //normal speed
// REG_EMC_SMCR1 = 0x0d221200; //fast speed
if (bus == 8) {
write_proc = nand_data_write8;
read_proc = nand_data_read8;
} else {
write_proc = nand_data_write16;
read_proc = nand_data_read16;
}
return 0;
}
int nand_fini_4740(void)
{
__nand_disable();
return 0;
}
/*
* Read oob <pagenum> pages from <startpage> page.
* Don't skip bad block.
* Don't use HW ECC.
*/
u32 nand_read_oob_4740(void *buf, u32 startpage, u32 pagenum)
{
u32 cnt, cur_page;
u8 *tmpbuf;
tmpbuf = (u8 *)buf;
cur_page = startpage;
cnt = 0;
while (cnt < pagenum) {
read_oob((void *)tmpbuf, oobsize, cur_page);
tmpbuf += oobsize;
cur_page++;
cnt++;
}
return cur_page;
}
static int nand_check_block(u32 block)
{
u32 pg,i;
if ( bad_block_page >= ppb ) //do absolute bad block detect!
{
pg = block * ppb + 0;
read_oob(oob_buf, oobsize, pg);
if ( oob_buf[0] != 0xff || oob_buf[1] != 0xff )
{
serial_puts("Absolute skip a bad block\n");
return 1;
}
pg = block * ppb + 1;
read_oob(oob_buf, oobsize, pg);
if ( oob_buf[0] != 0xff || oob_buf[1] != 0xff )
{
serial_puts("Absolute skip a bad block\n");
return 1;
}
pg = block * ppb + ppb - 2 ;
read_oob(oob_buf, oobsize, pg);
if ( oob_buf[0] != 0xff || oob_buf[1] != 0xff )
{
serial_puts("Absolute skip a bad block\n");
return 1;
}
pg = block * ppb + ppb - 1 ;
read_oob(oob_buf, oobsize, pg);
if ( oob_buf[0] != 0xff || oob_buf[1] != 0xff )
{
serial_puts("Absolute skip a bad block\n");
return 1;
}
}
else
{
pg = block * ppb + bad_block_page;
read_oob(oob_buf, oobsize, pg);
if (oob_buf[bad_block_pos] != 0xff)
{
serial_puts("Skip a bad block\n");
return 1;
}
}
return 0;
}
/*
* Read data <pagenum> pages from <startpage> page.
* Don't skip bad block.
* Don't use HW ECC.
*/
u32 nand_read_raw_4740(void *buf, u32 startpage, u32 pagenum, int option)
{
u32 cnt, j;
u32 cur_page, rowaddr;
u8 *tmpbuf;
tmpbuf = (u8 *)buf;
cur_page = startpage;
cnt = 0;
while (cnt < pagenum) {
select_chip(cnt / ppb);
if ((cur_page % ppb) == 0) {
if (nand_check_block(cur_page / ppb)) {
cur_page += ppb; // Bad block, set to next block
continue;
}
}
__nand_cmd(CMD_READA);
__nand_addr(0);
if (pagesize != 512)
__nand_addr(0);
rowaddr = cur_page;
for (j = 0; j < row; j++) {
__nand_addr(rowaddr & 0xff);
rowaddr >>= 8;
}
if (pagesize != 512)
__nand_cmd(CMD_CONFIRM);
__nand_sync();
read_proc(tmpbuf, pagesize);
tmpbuf += pagesize;
if (option != NO_OOB)
{
read_oob(tmpbuf, oobsize, cur_page);
tmpbuf += oobsize;
}
cur_page++;
cnt++;
}
return cur_page;
}
u32 nand_erase_4740(int blk_num, int sblk, int force)
{
int i, j;
u32 cur, rowaddr;
if (wp_pin)
__gpio_set_pin(wp_pin);
cur = sblk * ppb;
for (i = 0; i < blk_num; ) {
rowaddr = cur;
select_chip(cur / ppb);
if ( !force )
{
if (nand_check_block(cur/ppb))
{
cur += ppb;
blk_num += Hand.nand_plane;
continue;
}
}
__nand_cmd(CMD_E