📄 nand1.c
字号:
#include "nand.h"
#define MAJOR_NR 31
/* #define MAJOR_NR 60 */
#define ROM_MINOR 8 /* ro ROM card */
#define FLASH_MINOR 16 /* rw Flash card */
/* Don't touch the MAX_CHIPS until the hardcoded chip0 and chip1 are
removed from the code. */
#define MAX_CHIPS 2
#define MAX_PARTITIONS 8
#define EWP 100
#define EIO 200
#define EINVAL 400
void udelay(int tick)
{
int i;
for(i=0; i<tick*100;i++);
}
void set_cs()
{
*(unsigned char *)0x8080000c=0x00;
}
void clear_cs()
{
*(unsigned char *)0x8080000c=0x01;
}
void set_addr()
{
*(unsigned char *)0x80800004=0x01;
*(unsigned char *)0x80800008=0x00;
}
void clear_addr()
{
*(unsigned char *)0x80800004=0x00;
}
void set_command()
{
*(unsigned char *)0x80800008=0x01;
*(unsigned char *)0x80800004=0x00;
}
void clear_command()
{
*(unsigned char *)0x80800008=0x00;
}
void read_nandid()
{
unsigned char id[2];
set_cs();
set_command();
*(unsigned char *)DATA_ADDR = NAND_CMD_READID;
clear_command();
set_addr();
*(unsigned char *)DATA_ADDR = 0x00;
//*(unsigned char *)0x80800004=0x00;
clear_addr();
id[0]=*(unsigned char *)DATA_ADDR;
id[1]=*(unsigned char *)DATA_ADDR;
clear_cs();
}
int nand_write(unsigned long to, int len,int *retlen, unsigned char *buf)
{
int i,cnt,status;
unsigned char tmpbuf[OOBBLOCK+OOBSIZE];
unsigned long page;
unsigned long col;
unsigned short tmpvalue=0;
set_cs();
page = ((int) to) >> PAGE_SHIFT;
/* Get the starting column */
col = to & (OOBBLOCK - 1);
// set_cs();
/* Check the WP bit */
nand_command (NAND_CMD_STATUS, -1, -1);
if (!(*(unsigned char *)DATA_ADDR &0x80)){
return -EWP;
}
*retlen = 0;
/* Loop until all data is written */
while (*retlen < len) {
/* 将数据写到缓冲区中,判断是否超出一页 */
if ((col + len) >= OOBBLOCK)
for(i=col, cnt=0 ; i < OOBBLOCK ; i++, cnt++)
tmpbuf[i] = buf[(*retlen + cnt)];
else
for(i=col, cnt=0 ; cnt < (len - *retlen) ; i++, cnt++)
tmpbuf[i] = buf[(*retlen + cnt)];
/* Write ones for partial page programming */
for (i=OOBBLOCK ; i < (OOBBLOCK+OOBSIZE) ; i++)
tmpbuf[i] = 0xff;
/* Write pre-padding bytes into buffer */
for (i=0 ; i < col ; i++)
tmpbuf[i] = 0xff;
/* Write post-padding bytes into buffer */
if ((col + (len - *retlen)) < OOBBLOCK) {
for(i=(col + cnt) ; i < OOBBLOCK ; i++)
tmpbuf[i] = 0xff;
}
/* Send command to begin auto page programming */
nand_command (NAND_CMD_SEQIN, 0x00, page);
/* Write out complete page of data */
for (i=0 ; i < (OOBBLOCK+OOBSIZE) ; i++)
*(unsigned char *)DATA_ADDR=tmpbuf[i];
/* Send command to actually program the data */
nand_command (NAND_CMD_PAGEPROG, -1, -1);
/*
* Wait for program operation to complete. This could
* take up to 3000us (3ms) on some devices, so we try
* and exit as quickly as possible.
*/
status = 0;
for (i=0 ; i<24 ; i++) {
/* Delay for 125us */
udelay (12500);
/* Check the status */
nand_command (NAND_CMD_STATUS, -1, -1);
// status = (int) readb (nand->io_addr);
status = (int) *(unsigned char* )DATA_ADDR;
if (status & 0x40)
break;
}
/* See if device thinks it succeeded */
if (status & 0x01) {
return -EIO;
}
/*验证写的是否正确*/
/* Send command to read back the page */
if (col < ECCSIZE)
nand_command (NAND_CMD_READ0, col, page);
else
nand_command (NAND_CMD_READ1, col - 256, page);
/* Loop through and verify the data */
for (i=col ; i < cnt ; i++) {
tmpvalue=*(unsigned char* )DATA_ADDR;
// if (tmpbuf[i] != *(unsigned char* )DATA_ADDR) {
// return -EIO;
// }
}
/*
* If we are writing a large amount of data and/or it
* crosses page or half-page boundaries, we set the
* the column to zero. It simplifies the program logic.
*/
if (col)
col = 0x00;
/* Update written bytes count */
*retlen += cnt;
/* Increment page address */
page++;
}
/* Return happy */
*retlen = len;
return 0;
}
int nand_read (unsigned long from, int len, int *retlen, unsigned char *buf)
{
int j, state;
unsigned long page;
unsigned long col;
int erase_state = 0;
unsigned char oobvalue;
/* Do not allow reads past end of device */
if ((from + len) > NAND_SIZE) {
*retlen = 0;
return -EINVAL;
}
/* First we calculate the starting page */
page = from >> PAGE_SHIFT;
/* Get raw starting column */
col = from & (OOBBLOCK - 1);
/* State machine for devices having pages larger than 256 bytes */
state = (col < ECCSIZE) ? 0 : 1;
/* Calculate column address within ECC block context */
col = (col >= ECCSIZE) ? (col - ECCSIZE) : col;
/* Initialize return value */
*retlen = 0;
set_cs();
/* Loop until all data read */
while (*retlen < len) {
/* Send the read command */
if (!state)
nand_command (NAND_CMD_READ0, col, page);
else
nand_command (NAND_CMD_READ1, col, page);
/* Read the data directly into the return buffer */ //第一个256字节
if ((*retlen + (ECCSIZE - col)) >= len) {
while (*retlen < len)
{
// *(nand->io_addr)=from+(*retlen);
/* oobvalue=*(unsigned char*)DATA_ADDR;
(*retlen)++;
if(oobvalue ==0xff)
{
buf[(*retlen)]=oobvalue;
}
*/ buf[(*retlen)++] =*(unsigned char*)DATA_ADDR;
}
// buf[(*retlen)++] = readb (nand->IO_ADDR);
/* We're done */
continue;
}
else//第二个256字节
{
for (j=col ; j < ECCSIZE ; j++)
{
/* oobvalue=*(unsigned char*)DATA_ADDR;
(*retlen)++;
*/ buf[(*retlen)++] =*(unsigned char*)DATA_ADDR;
}
}
/*
* If the amount of data to be read is greater than
* (256 - col), then all subsequent reads will take
* place on page or half-page (in the case of 512 byte
* page devices) aligned boundaries and the column
* address will be zero. Setting the column address to
* to zero after the first read allows us to simplify
* the reading of data and the if/else statements above.
*/
if (col)
col = 0x00;
/* Increment page address */
if ((OOBBLOCK == 256) || state)
page++;
/* Toggle state machine */
if (OOBBLOCK == 512)
state = state ? 0 : 1;
}
clear_cs();
/* Return happy */
return 0;
}
/*
* Send command to NAND device
*/
void nand_command (unsigned int command, unsigned char column, unsigned short page_addr)
{
/*
* Write out the command to the device.
*/
set_command();
if (command != NAND_CMD_SEQIN)
*(unsigned char*)CTRL_ADDR = command;
// writeb (command, NAND_IO_ADDR);
else {
if (OOBBLOCK == 256 && column >= 256) {
column -= 256;
*(unsigned char*)CTRL_ADDR=NAND_CMD_RESET;
*(unsigned char*)(CTRL_ADDR)=NAND_CMD_READOOB;
*(unsigned char*)(CTRL_ADDR)=NAND_CMD_SEQIN;
/* writeb(NAND_CMD_RESET, NAND_IO_ADDR);
writeb(NAND_CMD_READOOB, NAND_IO_ADDR);
writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
*/ }
else if (OOBBLOCK == 512 && column >= 256) {
if (column < 512) {
column -= 256;
*(unsigned char*)(CTRL_ADDR)=NAND_CMD_READ1;
*(unsigned char*)(CTRL_ADDR)=NAND_CMD_SEQIN;
/* writeb(NAND_CMD_READ1, NAND_IO_ADDR);
writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
*/ }
else {
column -= 512;
*(unsigned char*)(CTRL_ADDR)=NAND_CMD_READOOB;
*(unsigned char*)(CTRL_ADDR)=NAND_CMD_SEQIN;
/* writeb(NAND_CMD_READOOB, NAND_IO_ADDR);
writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
*/ }
}
else {
*(unsigned char*)(CTRL_ADDR)=NAND_CMD_READ0;
*(unsigned char*)(CTRL_ADDR)=NAND_CMD_SEQIN;
/* writeb(NAND_CMD_READ0, NAND_IO_ADDR);
writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
*/ }
}
clear_command();
set_addr();
/* Serially input address */
if (column != -1)
*(unsigned char*)(IO_ADDR)=column;
// writeb (column, NAND_IO_ADDR);
if (page_addr != -1) {
*(unsigned char*)(IO_ADDR)=(unsigned char) (page_addr & 0xff);
*(unsigned char*)(IO_ADDR)=(unsigned char) ((page_addr >> 8) & 0xff);
/* writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);
writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);
*/ /* One more address cycle for higher density devices */
// writeb ((unsigned char) ((page_addr >> 16) & 0x0f),
// NAND_IO_ADDR);
}
clear_addr();
/* Pause for 15us */
udelay (15);
}
/*
* NAND erase a block
*/
int nand_erase (unsigned long startaddr,int eraselen)
{
int i, len, status, pages_per_block;
unsigned long page;
/* Start address must align on block boundary */
if (startaddr & (EARSE_SIZE - 1)) {
return -EINVAL;
}
/* Length must align on block boundary */
if (eraselen & (EARSE_SIZE - 1)) {
return -EINVAL;
}
/* Do not allow erase past end of device */
if ((eraselen + startaddr) > NAND_SIZE) {
return -EINVAL;
}
/* Shift to get first page */
page = (int) (startaddr >> PAGE_SHIFT);
/* Calculate pages in each block */
pages_per_block = EARSE_SIZE / OOBBLOCK;
/* Check the WP bit */
nand_command (NAND_CMD_STATUS, -1, -1);
if (!(*(unsigned char*)(DATA_ADDR) & 0x8080)) {
return -EIO;
}
/* Loop through the pages */
len = eraselen;
while (len) {
/* Send commands to erase a page */
nand_command( NAND_CMD_ERASE1, -1, page);
nand_command( NAND_CMD_ERASE2, -1, -1);
/*
* Wait for program operation to complete. nand could
* take up to 4000us (4ms) on some devices, so we try
* and exit as quickly as possible.
*/
status = 0;
for (i=0 ; i<32 ; i++) {
/* Delay for 125us */
udelay (125);
/* Check the status */
nand_command ( NAND_CMD_STATUS, -1, -1);
//status = (int) readb (nand->io_addr);
status = (int) *(unsigned char*)(DATA_ADDR);
if (status & 0x40)
break;
}
/* See if block erase succeeded */
if (status & 0x01) {
return -EIO;
}
/* Increment page address and decrement length */
len -= EARSE_SIZE;
page += pages_per_block;
}
/* Return happy */
return 0;
}
/*
* NAND read out-of-band
*/
int nand_read_oob (unsigned long from, int len, int *retlen, unsigned char *buf)
{
int i, col, page;
unsigned char oobvalue[16];
/* Shift to get page */
page = ((int) from) >> PAGE_SHIFT;
/* Mask to get column */
col = from & 0x0000000f;
/* Initialize return length value */
*retlen = 0;
if ((col + len) > OOBSIZE) {
return -EINVAL;
}
for (i = 0 ; i < len ; i++)
{
oobvalue[i]=0xaa;
}
/* Send the read command */
nand_command ( NAND_CMD_READOOB, col, page);
/* Read the data */
for (i = 0 ; i < len ; i++)
{
buf[i] = *(unsigned char*)(DATA_ADDR);
}
//
/* Return happy */
*retlen = len;
return 0;
}
/*test program for nand driver*/
void main()
{
unsigned char wdbuf[64],rdbuf[528];
int i;
unsigned long startaddr,oobaddr;
int retlen[1] ;
int earsesize=EARSE_SIZE;
unsigned char ch[2];
*(unsigned short *)0x00c20002=0x3c03;
#if 0
for(;;)
{
*(unsigned char *)0x80800000=0x55;
*(unsigned char *)0x80800000=0xaa;
*(unsigned char *)0x80800004=0x55;
*(unsigned char *)0x80800008=0xaa;
*(unsigned char *)0x8080000c=0x55;
*(unsigned char *)0x8080000c=0xaa;
ch[0]=*(unsigned char *)0x80800000;
ch[2]=*(unsigned char *)0x80800000;
}
# endif
* retlen = 0;
for (i=0;i<64 ;i++ )
{
wdbuf[i]=0x31+i%10;
rdbuf[i]=0;
}
startaddr = 512;
//for(;;)
read_nandid();
set_cs();
for (i=0;i<528 ;i++ )
{
rdbuf[i]=2;
}
oobaddr=1024;
// nand_read_oob(oobaddr,16,retlen,&rdbuf[0]);
nand_write( startaddr, 64, retlen, &wdbuf[0]);
nand_read( startaddr, 128, retlen, &rdbuf[0]);
for (i=0;i<16 ;i++ )
{
if(rdbuf[i]!=0xff)
rdbuf[i]=0;
}
/* startaddr = 0x0100;
// nand_write( startaddr, 64, retlen, wdbuf);
nand_read( startaddr, 64, retlen, rdbuf);
for (i=0;i<64 ;i++ )
{
rdbuf[i]=0;
}
startaddr = 0x0200;
nand_write( startaddr, 64, retlen, wdbuf);
nand_read( startaddr, 64, retlen, rdbuf);
startaddr = 0x0000;
nand_erase( startaddr, earsesize);
*/
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -