1
0
mirror of git://projects.qi-hardware.com/xburst-tools.git synced 2025-04-21 12:27:27 +03:00

change the usbboot to ingenic-tools (host side code)

add usb-boot to keep the device code.
This commit is contained in:
xiangfu
2009-04-11 15:04:25 +00:00
parent fa30f9e437
commit a3b9071e22
33 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,811 @@
#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_ERASE_SETUP);
for (j = 0; j < row; j++) {
__nand_addr(rowaddr & 0xff);
rowaddr >>= 8;
}
__nand_cmd(CMD_ERASE);
__nand_sync();
__nand_cmd(CMD_READ_STATUS);
if (__nand_data8() & 0x01)
{
serial_puts("Erase fail at ");
serial_put_hex(cur / ppb);
nand_mark_bad_4740(cur/ppb);
cur += ppb;
blk_num += Hand.nand_plane;
continue;
}
cur += ppb;
i++;
}
if (wp_pin)
__gpio_clear_pin(wp_pin);
return cur;
}
static int read_oob(void *buf, u32 size, u32 pg)
{
u32 i, coladdr, rowaddr;
select_chip(pg / ppb);
if (pagesize == 512)
coladdr = 0;
else
coladdr = pagesize;
if (pagesize == 512)
/* Send READOOB command */
__nand_cmd(CMD_READC);
else
/* Send READ0 command */
__nand_cmd(CMD_READA);
/* Send column address */
__nand_addr(coladdr & 0xff);
if (pagesize != 512)
__nand_addr(coladdr >> 8);
/* Send page address */
rowaddr = pg;
for (i = 0; i < row; i++) {
__nand_addr(rowaddr & 0xff);
rowaddr >>= 8;
}
/* Send READSTART command for 2048 ps NAND */
if (pagesize != 512)
__nand_cmd(CMD_CONFIRM);
/* Wait for device ready */
__nand_sync();
/* Read oob data */
read_proc(buf, size);
if (pagesize == 512)
__nand_sync();
return 0;
}
void rs_correct(unsigned char *buf, int idx, int mask)
{
int i, j;
unsigned short d, d1, dm;
i = (idx * 9) >> 3;
j = (idx * 9) & 0x7;
i = (j == 0) ? (i - 1) : i;
j = (j == 0) ? 7 : (j - 1);
d = (buf[i] << 8) | buf[i - 1];
d1 = (d >> j) & 0x1ff;
d1 ^= mask;
dm = ~(0x1ff << j);
d = (d & dm) | (d1 << j);
buf[i - 1] = d & 0xff;
buf[i] = (d >> 8) & 0xff;
}
/*
* Read data <pagenum> pages from <startpage> page.
* Skip bad block if detected.
* HW ECC is used.
*/
u32 nand_read_4740(void *buf, u32 startpage, u32 pagenum, int option)
{
u32 j, k;
u32 cur_page, cur_blk, cnt, rowaddr, ecccnt;
u8 *tmpbuf,flag = 0;
ecccnt = pagesize / ECC_BLOCK;
cur_page = startpage;
cnt = 0;
tmpbuf = buf;
handshake_PKT[3] = 0;
while (cnt < pagenum) {
select_chip(cnt / ppb);
/* If this is the first page of the block, check for bad. */
if ((cur_page % ppb) == 0) {
cur_blk = cur_page / ppb;
if (nand_check_block(cur_blk)) {
cur_page += ppb; // Bad block, set to next block
continue;
}
}
/* read oob first */
read_oob(oob_buf, oobsize, cur_page);
__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();
for (j = 0; j < ecccnt; j++) {
volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0;
u32 stat;
flag = 0;
REG_EMC_NFINTS = 0x0;
__nand_ecc_rs_decoding();
read_proc(tmpbuf, ECC_BLOCK);
for (k = 0; k < PAR_SIZE; k++) {
*paraddr++ = oob_buf[ecc_pos + j*PAR_SIZE + k];
if (oob_buf[ecc_pos + j*PAR_SIZE + k] != 0xff)
flag = 1;
}
REG_EMC_NFECR |= EMC_NFECR_PRDY;
__nand_ecc_decode_sync();
__nand_ecc_disable();
/* Check decoding */
stat = REG_EMC_NFINTS;
if (stat & EMC_NFINTS_ERR) {
if (stat & EMC_NFINTS_UNCOR) {
if (flag)
{
// serial_puts("\nUncorrectable error occurred\n");
// serial_put_hex(cur_page);
handshake_PKT[3] = 1;
}
}
else {
handshake_PKT[3] = 0;
u32 errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT;
switch (errcnt) {
case 4:
rs_correct(tmpbuf, (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT);
case 3:
rs_correct(tmpbuf, (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT);
case 2:
rs_correct(tmpbuf, (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT);
case 1:
rs_correct(tmpbuf, (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT);
break;
default:
break;
}
}
}
/* increment pointer */
tmpbuf += ECC_BLOCK;
}
switch (option)
{
case OOB_ECC:
for (j = 0; j < oobsize; j++)
tmpbuf[j] = oob_buf[j];
tmpbuf += oobsize;
break;
case OOB_NO_ECC:
for (j = 0; j < ecccnt * PAR_SIZE; j++)
oob_buf[ecc_pos + j] = 0xff;
for (j = 0; j < oobsize; j++)
tmpbuf[j] = oob_buf[j];
tmpbuf += oobsize;
break;
case NO_OOB:
break;
}
cur_page++;
cnt++;
}
return cur_page;
}
u32 nand_program_4740(void *context, int spage, int pages, int option)
{
u32 i, j, cur, rowaddr;
u8 *tmpbuf;
u32 ecccnt,oobsize_sav,ecccnt_sav,eccpos_sav;
u8 ecc_buf[256];
if (wp_pin)
__gpio_set_pin(wp_pin);
restart:
tmpbuf = (u8 *)context;
ecccnt_sav = ecccnt = pagesize / ECC_BLOCK;
oobsize_sav = oobsize;
eccpos_sav = ecc_pos;
i = 0;
cur = spage;
while (i < pages) {
select_chip(cur / ppb);
#if 1
if ((pagesize == 4096) && (cur < 8)) {
ecccnt = 4;
oobsize = 64;
ecc_pos = 6;
} else {
ecccnt = ecccnt_sav;
oobsize = oobsize_sav;
ecc_pos = eccpos_sav;
}
/* Skip 16KB after nand_spl if pagesize=4096 */
if ((pagesize == 4096) && (cur == 8))
tmpbuf += 16 * 1024;
#endif
if ((cur % ppb) == 0) {
if (nand_check_block(cur / ppb)) {
cur += ppb; // Bad block, set to next block
continue;
}
}
if ( option != NO_OOB ) //if NO_OOB do not perform vaild check!
{
for ( j = 0 ; j < pagesize + oobsize; j ++)
{
if (tmpbuf[j] != 0xff)
break;
}
if ( j == oobsize + pagesize )
{
tmpbuf += ( pagesize + oobsize ) ;
i ++;
cur ++;
continue;
}
}
if (pagesize == 512)
__nand_cmd(CMD_READA);
__nand_cmd(CMD_SEQIN);
__nand_addr(0);
if (pagesize != 512)
__nand_addr(0);
rowaddr = cur;
for (j = 0; j < row; j++) {
__nand_addr(rowaddr & 0xff);
rowaddr >>= 8;
}
switch (option)
{
case OOB_ECC:
write_proc(tmpbuf, pagesize); //write data
tmpbuf += pagesize;
write_proc((u8 *)tmpbuf, oobsize); //write oob
tmpbuf += oobsize;
break;
case OOB_NO_ECC:
for (j = 0; j < ecccnt; j++) {
volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0;
int k;
REG_EMC_NFINTS = 0x0;
__nand_ecc_rs_encoding();
write_proc(tmpbuf, ECC_BLOCK);
__nand_ecc_encode_sync();
__nand_ecc_disable();
/* Read PAR values */
for (k = 0; k < PAR_SIZE; k++) {
ecc_buf[j*PAR_SIZE+k] = *paraddr++;
}
tmpbuf += ECC_BLOCK;
}
for (j = 0; j < oobsize; j++) {
oob_buf[j] = tmpbuf[j];
}
for (j = 0; j < ecccnt*PAR_SIZE; j++)
oob_buf[ecc_pos + j] = ecc_buf[j];
write_proc((u8 *)oob_buf, oobsize);
tmpbuf += oobsize;
break;
case NO_OOB: //bin image
/* write out data */
for (j = 0; j < ecccnt; j++) {
volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0;
int k;
REG_EMC_NFINTS = 0x0;
__nand_ecc_rs_encoding();
write_proc(tmpbuf, ECC_BLOCK);
__nand_ecc_encode_sync();
__nand_ecc_disable();
/* Read PAR values */
for (k = 0; k < PAR_SIZE; k++) {
ecc_buf[j*PAR_SIZE+k] = *paraddr++;
}
tmpbuf += ECC_BLOCK;
}
for (j = 0; j < oobsize; j++) {
oob_buf[j] = 0xff;
}
oob_buf[2] = 0;
oob_buf[3] = 0;
oob_buf[4] = 0;
for (j = 0; j < ecccnt*PAR_SIZE; j++) {
oob_buf[ecc_pos + j] = ecc_buf[j];
}
write_proc((u8 *)oob_buf, oobsize);
break;
}
/* send program confirm command */
__nand_cmd(CMD_PGPROG);
__nand_sync();
__nand_cmd(CMD_READ_STATUS);
if (__nand_data8() & 0x01) /* page program error */
{
serial_puts("Skip a write fail block\n");
nand_erase_4740( 1, cur/ppb, 1); //force erase before
nand_mark_bad_4740(cur/ppb);
spage += ppb;
goto restart;
}
i ++;
cur ++;
}
if (wp_pin)
__gpio_clear_pin(wp_pin);
ecccnt = ecccnt_sav;
oobsize = oobsize_sav;
ecc_pos = eccpos_sav;
return cur;
}
static u32 nand_mark_bad_page(u32 page)
{
u8 badbuf[4096 + 128];
u32 i;
if (wp_pin)
__gpio_set_pin(wp_pin);
//all set to 0x00
for (i = 0; i < pagesize + oobsize; i++)
badbuf[i] = 0x00;
__nand_cmd(CMD_READA);
__nand_cmd(CMD_SEQIN);
__nand_addr(0);
if (pagesize != 512)
__nand_addr(0);
for (i = 0; i < row; i++) {
__nand_addr(page & 0xff);
page >>= 8;
}
write_proc((char *)badbuf, pagesize + oobsize);
__nand_cmd(CMD_PGPROG);
__nand_sync();
if (wp_pin)
__gpio_clear_pin(wp_pin);
return page;
}
u32 nand_mark_bad_4740(int block)
{
u32 rowaddr;
// nand_erase_4740( 1, block, 1); //force erase before
if ( bad_block_page >= ppb ) //absolute bad block mark!
{ //mark four page!
rowaddr = block * ppb + 0;
nand_mark_bad_page(rowaddr);
rowaddr = block * ppb + 1;
nand_mark_bad_page(rowaddr);
rowaddr = block * ppb + ppb - 2;
nand_mark_bad_page(rowaddr);
rowaddr = block * ppb + ppb - 1;
nand_mark_bad_page(rowaddr);
}
else //mark one page only
{
rowaddr = block * ppb + bad_block_page;
nand_mark_bad_page(rowaddr);
}
return rowaddr;
}
static int nand_data_write8(char *buf, int count)
{
int i;
u8 *p = (u8 *)buf;
for (i=0;i<count;i++)
__nand_data8() = *p++;
return 0;
}
static int nand_data_write16(char *buf, int count)
{
int i;
u16 *p = (u16 *)buf;
for (i=0;i<count/2;i++)
__nand_data16() = *p++;
return 0;
}
static int nand_data_read8(char *buf, int count)
{
int i;
u8 *p = (u8 *)buf;
for (i=0;i<count;i++)
*p++ = __nand_data8();
return 0;
}
static int nand_data_read16(char *buf, int count)
{
int i;
u16 *p = (u16 *)buf;
for (i=0;i<count/2;i++)
*p++ = __nand_data16();
return 0;
}

View File

@@ -0,0 +1,826 @@
#include "jz4750.h"
#include "nandflash.h"
#include "usb_boot.h"
#include "hand.h"
#define dprintf(n...)
#define __nand_enable() (REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1)
#define __nand_disable() (REG_EMC_NFCSR &= ~(EMC_NFCSR_NFCE1))
#define __nand_ready() ((REG_GPIO_PXPIN(2) & 0x08000000) ? 1 : 0)
#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 ECC_BLOCK 512
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 = 512, oobsize = 16, ppb = 32;
static int eccpos = 3, bchbit = 8, par_size = 13, forceerase = 1, wp_pin;
static int oobfs = 0; /* 1:store file system information in oob, 0:don't store */
static int oobecc = 0; /* Whether the oob data of the binary contains ECC data? */
static int bad_block_page = 127; /* Specify the page number of badblock flag inside a block */
static u32 bad_block_pos = 0; /* Specify the badblock flag offset inside the oob */
static u8 oob_buf[256] = {0};
extern hand_t Hand;
extern u16 handshake_PKT[4];
static inline void __nand_sync(void)
{
unsigned int timeout = 10000;
while ((REG_GPIO_PXPIN(2) & 0x08000000) && timeout--);
while (!(REG_GPIO_PXPIN(2) & 0x08000000));
}
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;
inline void nand_enable_4750(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_4750(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_4750(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_4750(int bus_width, int row_cycle, int page_size, int page_per_block,
int bch_bit, int ecc_pos, int bad_pos, int bad_page, int force)
{
bus = bus_width;
row = row_cycle;
pagesize = page_size;
oobsize = pagesize / 32;
ppb = page_per_block;
bchbit = bch_bit;
forceerase = force;
eccpos = ecc_pos;
bad_block_pos = bad_pos;
bad_block_page = bad_page;
wp_pin = Hand.nand_wppin;
if (bchbit == 8)
par_size = 13;
else
par_size = 7;
#if 0
gpio_base = (u8 *)gbase;
emc_base = (u8 *)ebase;
addrport = (u8 *)aport;
dataport = (u8 *)dport;
cmdport = (u8 *)cport;
#endif
/* Initialize NAND Flash Pins */
if (bus == 8) {
__gpio_as_nand_8bit();
}
if (wp_pin)
{
__gpio_as_output(wp_pin);
__gpio_disable_pull(wp_pin);
}
__nand_enable();
// REG_EMC_SMCR1 = 0x0fff7700; //slow speed
REG_EMC_SMCR1 = 0x04444400; //normal speed
// REG_EMC_SMCR1 = 0x0d221200; //fast speed
/* If ECCPOS isn't configured in config file, the initial value is 0 */
if (eccpos == 0) {
eccpos = 3;
}
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_4750(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_4750(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");
serial_put_hex(block);
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");
serial_put_hex(block);
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");
serial_put_hex(block);
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");
serial_put_hex(block);
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 at");
serial_put_hex(block);
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_4750(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) {
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_4750(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; i++) {
rowaddr = cur;
if (!force) /* if set, erase anything */
if (nand_check_block(cur/ppb))
{
cur += ppb;
blk_num += Hand.nand_plane;
continue;
}
__nand_cmd(CMD_ERASE_SETUP);
for (j = 0; j < row; j++) {
__nand_addr(rowaddr & 0xff);
rowaddr >>= 8;
}
__nand_cmd(CMD_ERASE);
__nand_sync();
__nand_cmd(CMD_READ_STATUS);
if (__nand_data8() & 0x01)
{
serial_puts("Erase fail at ");
serial_put_hex(cur / ppb);
nand_mark_bad_4750(cur / ppb);
cur += ppb;
blk_num += Hand.nand_plane;
continue;
}
cur += ppb;
}
if (wp_pin)
__gpio_clear_pin(wp_pin);
return cur;
}
static int read_oob(void *buf, u32 size, u32 pg)
{
u32 i, coladdr, rowaddr;
if (pagesize == 512)
coladdr = 0;
else
coladdr = pagesize;
if (pagesize == 512)
/* Send READOOB command */
__nand_cmd(CMD_READC);
else
/* Send READ0 command */
__nand_cmd(CMD_READA);
/* Send column address */
__nand_addr(coladdr & 0xff);
if (pagesize != 512)
__nand_addr(coladdr >> 8);
/* Send page address */
rowaddr = pg;
for (i = 0; i < row; i++) {
__nand_addr(rowaddr & 0xff);
rowaddr >>= 8;
}
/* Send READSTART command for 2048 or 4096 ps NAND */
if (pagesize != 512)
__nand_cmd(CMD_CONFIRM);
/* Wait for device ready */
__nand_sync();
/* Read oob data */
read_proc(buf, size);
if (pagesize == 512)
__nand_sync();
return 0;
}
static void bch_correct(unsigned char *dat, int idx)
{
int i, bit; // the 'bit' of i byte is error
i = (idx - 1) >> 3;
bit = (idx - 1) & 0x7;
dat[i] ^= (1 << bit);
}
/*
* Read data <pagenum> pages from <startpage> page.
* Skip bad block if detected.
* HW ECC is used.
*/
u32 nand_read_4750(void *buf, u32 startpage, u32 pagenum, int option)
{
u32 j, k;
u32 cur_page, cur_blk, cnt, rowaddr, ecccnt;
u8 *tmpbuf, *p, flag = 0;
u32 oob_per_eccsize;
ecccnt = pagesize / ECC_BLOCK;
oob_per_eccsize = eccpos / ecccnt;
cur_page = startpage;
cnt = 0;
tmpbuf = buf;
while (cnt < pagenum) {
/* If this is the first page of the block, check for bad. */
if ((cur_page % ppb) == 0) {
cur_blk = cur_page / ppb;
if (nand_check_block(cur_blk)) {
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 data */
read_proc((char *)tmpbuf, pagesize);
/* read oob first */
read_proc((char *)oob_buf, oobsize);
for (j = 0; j < ecccnt; j++) {
u32 stat;
flag = 0;
REG_BCH_INTS = 0xffffffff;
if (cur_page >= 16384 / pagesize)
{
if (bchbit == 8)
{
__ecc_decoding_8bit();
par_size = 13;
}
else
{
__ecc_decoding_4bit();
par_size = 7;
}
}
else
{
__ecc_decoding_8bit();
par_size = 13;
}
if (option != NO_OOB)
__ecc_cnt_dec(ECC_BLOCK + oob_per_eccsize + par_size);
else
__ecc_cnt_dec(ECC_BLOCK + par_size);
for (k = 0; k < ECC_BLOCK; k++) {
REG_BCH_DR = tmpbuf[k];
}
if (option != NO_OOB) {
for (k = 0; k < oob_per_eccsize; k++) {
REG_BCH_DR = oob_buf[oob_per_eccsize * j + k];
}
}
for (k = 0; k < par_size; k++) {
REG_BCH_DR = oob_buf[eccpos + j*par_size + k];
if (oob_buf[eccpos + j*par_size + k] != 0xff)
flag = 1;
}
/* Wait for completion */
__ecc_decode_sync();
__ecc_disable();
/* Check decoding */
stat = REG_BCH_INTS;
if (stat & BCH_INTS_ERR) {
if (stat & BCH_INTS_UNCOR) {
if (flag)
{
dprintf("Uncorrectable ECC error occurred\n");
handshake_PKT[3] = 1;
}
}
else {
handshake_PKT[3] = 0;
unsigned int errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT;
switch (errcnt) {
case 8:
dprintf("correct 8th error\n");
bch_correct(tmpbuf, (REG_BCH_ERR3 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
case 7:
dprintf("correct 7th error\n");
bch_correct(tmpbuf, (REG_BCH_ERR3 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
case 6:
dprintf("correct 6th error\n");
bch_correct(tmpbuf, (REG_BCH_ERR2 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
case 5:
dprintf("correct 5th error\n");
bch_correct(tmpbuf, (REG_BCH_ERR2 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
case 4:
dprintf("correct 4th error\n");
bch_correct(tmpbuf, (REG_BCH_ERR1 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
case 3:
dprintf("correct 3th error\n");
bch_correct(tmpbuf, (REG_BCH_ERR1 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
case 2:
dprintf("correct 2th error\n");
bch_correct(tmpbuf, (REG_BCH_ERR0 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
case 1:
dprintf("correct 1th error\n");
bch_correct(tmpbuf, (REG_BCH_ERR0 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
break;
default:
dprintf("no error\n");
break;
}
}
}
/* increment pointer */
tmpbuf += ECC_BLOCK;
}
switch (option)
{
case OOB_ECC:
for (j = 0; j < oobsize; j++)
tmpbuf[j] = oob_buf[j];
tmpbuf += oobsize;
break;
case OOB_NO_ECC:
for (j = 0; j < par_size * ecccnt; j++)
oob_buf[eccpos + j] = 0xff;
for (j = 0; j < oobsize; j++)
tmpbuf[j] = oob_buf[j];
tmpbuf += oobsize;
break;
case NO_OOB:
break;
}
cur_page++;
cnt++;
}
return cur_page;
}
u32 nand_program_4750(void *context, int spage, int pages, int option)
{
u32 i, j, cur_page, cur_blk, rowaddr;
u8 *tmpbuf;
u32 ecccnt;
u8 ecc_buf[256];
u32 oob_per_eccsize;
int eccpos_sav = eccpos, bchbit_sav = bchbit, par_size_sav = par_size;
int spl_size = 16 * 1024 / pagesize;
if (wp_pin)
__gpio_set_pin(wp_pin);
restart:
tmpbuf = (u8 *)context;
ecccnt = pagesize / ECC_BLOCK;
oob_per_eccsize = eccpos / ecccnt;
i = 0;
cur_page = spage;
while (i < pages) {
if (cur_page < spl_size) {
bchbit = 8;
eccpos = 3;
par_size = 13;
} else if (cur_page >= spl_size) {
bchbit = bchbit_sav;
eccpos = eccpos_sav;
par_size = par_size_sav;
}
if ((cur_page % ppb) == 0) { /* First page of block, test BAD. */
if (nand_check_block(cur_page / ppb)) {
cur_page += ppb; /* Bad block, set to next block */
continue;
}
}
if ( option != NO_OOB ) //if NO_OOB do not perform vaild check!
{
for ( j = 0 ; j < pagesize + oobsize; j ++)
{
if (tmpbuf[j] != 0xff)
break;
}
if ( j == oobsize + pagesize )
{
tmpbuf += ( pagesize + oobsize ) ;
i ++;
cur_page ++;
continue;
}
}
if (pagesize == 512)
__nand_cmd(CMD_READA);
__nand_cmd(CMD_SEQIN);
__nand_addr(0);
if (pagesize != 512)
__nand_addr(0);
rowaddr = cur_page;
for (j = 0; j < row; j++) {
__nand_addr(rowaddr & 0xff);
rowaddr >>= 8;
}
/* write out data */
for (j = 0; j < ecccnt; j++) {
volatile u8 *paraddr;
int k;
paraddr = (volatile u8 *)BCH_PAR0;
REG_BCH_INTS = 0xffffffff;
if (bchbit == 8)
__ecc_encoding_8bit();
else
__ecc_encoding_4bit();
/* Set BCHCNT.DEC_COUNT to data block size in bytes */
if (option != NO_OOB)
__ecc_cnt_enc(ECC_BLOCK + oob_per_eccsize);
else
__ecc_cnt_enc(ECC_BLOCK);
/* Write data in data area to BCH */
for (k = 0; k < ECC_BLOCK; k++) {
REG_BCH_DR = tmpbuf[ECC_BLOCK * j + k];
}
/* Write file system information in oob area to BCH */
if (option != NO_OOB)
{
for (k = 0; k < oob_per_eccsize; k++) {
REG_BCH_DR = tmpbuf[pagesize + oob_per_eccsize * j + k];
}
}
__ecc_encode_sync();
__ecc_disable();
/* Read PAR values */
for (k = 0; k < par_size; k++) {
ecc_buf[j * par_size + k] = *paraddr++;
}
write_proc((char *)&tmpbuf[ECC_BLOCK * j], ECC_BLOCK);
}
switch (option)
{
case OOB_ECC:
case OOB_NO_ECC:
for (j = 0; j < eccpos; j++) {
oob_buf[j] = tmpbuf[pagesize + j];
}
for (j = 0; j < ecccnt * par_size; j++) {
oob_buf[eccpos + j] = ecc_buf[j];
}
tmpbuf += pagesize + oobsize;
break;
case NO_OOB: //bin image
for (j = 0; j < ecccnt * par_size; j++) {
oob_buf[eccpos + j] = ecc_buf[j];
}
tmpbuf += pagesize;
break;
}
/* write out oob buffer */
write_proc((u8 *)oob_buf, oobsize);
/* send program confirm command */
__nand_cmd(CMD_PGPROG);
__nand_sync();
__nand_cmd(CMD_READ_STATUS);
if (__nand_data8() & 0x01) /* page program error */
{
serial_puts("Skip a write fail block\n");
nand_erase_4750( 1, cur_page/ppb, 1); //force erase before
nand_mark_bad_4750(cur_page/ppb);
spage += ppb;
goto restart;
}
i++;
cur_page++;
}
if (wp_pin)
__gpio_clear_pin(wp_pin);
bchbit = bchbit_sav;
eccpos = eccpos_sav;
par_size = par_size_sav;
return cur_page;
}
static u32 nand_mark_bad_page(u32 page)
{
u8 badbuf[4096 + 128];
u32 i;
if (wp_pin)
__gpio_set_pin(wp_pin);
for (i = 0; i < pagesize + oobsize; i++)
badbuf[i] = 0x00;
//all set to 0x00
__nand_cmd(CMD_READA);
__nand_cmd(CMD_SEQIN);
__nand_addr(0);
if (pagesize != 512)
__nand_addr(0);
for (i = 0; i < row; i++) {
__nand_addr(page & 0xff);
page >>= 8;
}
write_proc((char *)badbuf, pagesize + oobsize);
__nand_cmd(CMD_PGPROG);
__nand_sync();
if (wp_pin)
__gpio_clear_pin(wp_pin);
return page;
}
u32 nand_mark_bad_4750(int block)
{
u32 rowaddr;
if ( bad_block_page >= ppb ) //absolute bad block mark!
{ //mark four page!
rowaddr = block * ppb + 0;
nand_mark_bad_page(rowaddr);
rowaddr = block * ppb + 1;
nand_mark_bad_page(rowaddr);
rowaddr = block * ppb + ppb - 2;
nand_mark_bad_page(rowaddr);
rowaddr = block * ppb + ppb - 1;
nand_mark_bad_page(rowaddr);
}
else //mark one page only
{
rowaddr = block * ppb + bad_block_page;
nand_mark_bad_page(rowaddr);
}
return rowaddr;
}
static int nand_data_write8(char *buf, int count)
{
int i;
u8 *p = (u8 *)buf;
for (i=0;i<count;i++)
__nand_data8() = *p++;
return 0;
}
static int nand_data_write16(char *buf, int count)
{
int i;
u16 *p = (u16 *)buf;
for (i=0;i<count/2;i++)
__nand_data16() = *p++;
return 0;
}
static int nand_data_read8(char *buf, int count)
{
int i;
u8 *p = (u8 *)buf;
for (i=0;i<count;i++)
*p++ = __nand_data8();
return 0;
}
static int nand_data_read16(char *buf, int count)
{
int i;
u16 *p = (u16 *)buf;
for (i=0;i<count/2;i++)
*p++ = __nand_data16();
return 0;
}