📄 nand.c
字号:
/*****************************************************************************;; (C) Unpublished Work of ADMtek Incorporated. All Rights Reserved.;; THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL,; PROPRIETARY AND TRADESECRET INFORMATION OF ADMTEK INCORPORATED.; ACCESS TO THIS WORK IS RESTRICTED TO (I) ADMTEK EMPLOYEES WHO HAVE A; NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF THEIR ASSIGNMENTS; AND (II) ENTITIES OTHER THAN ADMTEK WHO HAVE ENTERED INTO APPROPRIATE; LICENSE AGREEMENTS. NO PART OF THIS WORK MAY BE USED, PRACTICED,; PERFORMED, COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,; ABBRIDGED, CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,; TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF ADMTEK.; ANY USE OR EXPLOITATION OF THIS WORK WITHOUT AUTHORIZATION COULD; SUBJECT THE PERPERTRATOR TO CRIMINAL AND CIVIL LIABILITY.;;------------------------------------------------------------------------------;; Project : AM5120; Creator : Jeanson Hung; File : nand.c; Abstract:;;Modification History:; 10/8/2003, ProChao - support the bootloader to be compatible with the; NAND flash driver in application (Linux kernel);;;*****************************************************************************/#include <ctype.h>#include <mips.h>#include <mips4kc.h>#include <adm5120.h>/*#include <buart.h>*/#include "nand.h"#include "ftl-port.h" //porting by ProChao, 10/8/2003//---------------------------------------------------------volatile UINT8 *base = (UINT8 *) NAND_REG_BASE;static struct nand_oobinfo oobinfo_buf = { // init'ed by ProChao 1, {0, 1, 2, 3, 4, 5}};static u_char oob_databuf[NAND_PAGE_SIZE + NAND_PAGE_OOB_SIZE];// adds the verifying block that can be used before reading/writing(erasing) blockstatic void nand_hwcontrol(int cmd){ switch (cmd) { case NAND_CTL_SETCLE: *(base + NAND_SET_CLE) = 0x01; break; case NAND_CTL_CLRCLE: *(base + NAND_CLR_CLE) = 0x01; break; case NAND_CTL_SETALE: *(base + NAND_SET_ALE) = 0x01; break; case NAND_CTL_CLRALE: *(base + NAND_CLR_ALE) = 0x01; break; case NAND_CTL_SETNCE: *(base + NAND_SET_CEn) = 0x01; break; case NAND_CTL_CLRNCE: *(base + NAND_CLR_CEn) = 0x01; default: break; }}//static UINT8 nand_read_byte(void){ return *base; // readb( base);}static void nand_write_byte(UINT8 byte){ *base = byte; //writeb( byte, base);}//static void nand_select_chip(int chip){ switch (chip) { case -1: nand_hwcontrol(NAND_CTL_CLRNCE); break; case 0: nand_hwcontrol(NAND_CTL_SETNCE); break; default: // bugs, should not be here break; }}static void nand_write_buf(const u_char * buf, int len){ int i; for (i = 0; i < len; i++) *base = buf[i];}static void nand_read_buf(u_char * buf, int len){ int i; for (i = 0; i < len; i++) buf[i] = *base;}static int nand_verify_buf(const u_char * buf, int len){ int i; for (i = 0; i < len; i++) if (buf[i] != *base) return i; // return 0;}// send command to the NAND devicestatic void nand_command(unsigned command, int column, int page_addr){ UINT32 j; int readcmd; /* Begin command latch cycle */ nand_hwcontrol(NAND_CTL_SETCLE); /* Write out the command to the device. */ if (command == NAND_CMD_SEQIN) { if (column >= NAND_PAGE_SIZE) { /* OOB area */ column -= NAND_PAGE_SIZE; readcmd = NAND_CMD_READOOB; } else if (column < 256) { /* First 256 bytes --> READ0 */ readcmd = NAND_CMD_READ0; } else { // 2nd 256 bytes column -= 256; readcmd = NAND_CMD_READ1; } nand_write_byte(readcmd); } nand_write_byte(command); /* Set ALE and clear CLE to start address cycle */ nand_hwcontrol(NAND_CTL_CLRCLE); if (column != -1 || page_addr != -1) { nand_hwcontrol(NAND_CTL_SETALE); /* Serially input address */ if (column != -1) nand_write_byte(column); if (page_addr != -1) { nand_write_byte((unsigned char) (page_addr & 0xff)); nand_write_byte((unsigned char) ((page_addr >> 8) & 0xff)); /* One more address cycle for higher density devices */ if (NAND_FLASH_SIZE & 0x0c000000) // 32MB by ProChao, 10/8/2003 nand_write_byte((unsigned char) ((page_addr >> 16) & 0x0f)); } /* Latch in address */ nand_hwcontrol(NAND_CTL_CLRALE); } /* program and erase have their own busy handlers * status and sequential in needs no delay */ switch (command) { case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: return; case NAND_CMD_RESET: nand_hwcontrol(NAND_CTL_SETCLE); nand_write_byte(NAND_CMD_STATUS); nand_hwcontrol(NAND_CTL_CLRCLE); while (!(nand_read_byte() & 0x40)); return; /* This applies to read commands */ default: /* If we don't have access to the busy pin, we apply the given * command delay */ // wait 1us */ for (j = 0; j < 1000; j++); }}/* Appropriate chip should already be selected */static int nand_block_bad(unsigned long page){ UINT8 bad, status, status1; nand_command(NAND_CMD_READOOB, NAND_BADBLOCK_POS, page); bad = nand_read_byte(); if (bad != 0xff) { // might be programmed alrady with ECC info, so checking other status info status = nand_read_byte(); // readout next 2 octets status1 = nand_read_byte(); if (status != status1) // these 2 octets should be programmed same return 1; // this block is treated as the bad one // switch (status) { case SECTOR_FREE: // available for use case SECTOR_USED: // of course, for being used break; case SECTOR_IGNORED: case SECTOR_DELETED: default: return 1; // be treated as not-usable } } return 0;}/* * Wait for command done. This applies to erase and program only * Erase can take up to 400ms and program up to 20ms according to * general NAND and SmartMedia specs **/static int nand_wait(int state){ int i, j, status; int timeo; // HZ=100 ? if (state == FL_ERASING) timeo = 50; else timeo = 3;// spin_lock_bh (&this->chip_lock); nand_command(NAND_CMD_STATUS, -1, -1); while (timeo--) { //delay for a while before reading for (i = 0; i < 10; i++) //10us for (j = 0; j < 1000; j++); // if (nand_read_byte() & 0x40) //status ready break; } status = (int) nand_read_byte(); return status;}/* * Nand_page_program function is used for write and writev ! * This function will always program a full page of data * If you call it with a non page aligned buffer, you're lost :) */static int nand_write_page(u_char * data_poi, int page, u_char * oob_buf, struct nand_oobinfo *oobsel){ int i, status; u_char ecc_code[6], *oob_data; /* force SOFT_ECC given by NAND driver, Jeanson */ int eccmode = NAND_ECC_SOFT; //this->eccmode; int *oob_config = oobsel->eccpos; /* pad oob area, if we have no oob buffer from fs-driver */ if (!oob_buf) { oob_data = &oob_databuf[NAND_PAGE_SIZE]; //this->data_buf[ mtd->oobblock]; for (i = 0; i < NAND_PAGE_OOB_SIZE; i++) oob_data[i] = 0xff; } else oob_data = oob_buf; /* Send command to begin auto page programming */ nand_command(NAND_CMD_SEQIN, 0x00, page); /* Pull high SPn, Jeanson */ *(base + NAND_CLR_SPn_REG) = 1; /* Write out complete page of data, take care of eccmode */ switch (eccmode) { /* No ecc and software ecc 3/256, write all */ case NAND_ECC_NONE:// printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); nand_write_buf(data_poi, NAND_PAGE_SIZE); break; case NAND_ECC_SOFT: nand_calculate_ecc(&data_poi[0], &ecc_code[0]); for (i = 0; i < 3; i++) oob_data[oob_config[i]] = ecc_code[i]; /* Calculate and write the second ECC for 512 Byte page size */ // must be 512-byte page size nand_calculate_ecc(&data_poi[256], &ecc_code[3]); for (i = 3; i < 6; i++) oob_data[oob_config[i]] = ecc_code[i]; // nand_write_buf(data_poi, NAND_PAGE_SIZE); break; default:// printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);// BUG(); break; } /* Pull low SPn, Jeanson */ *(base + NAND_SET_SPn_REG) = 1; /* Write out OOB data */ nand_write_buf(oob_data, NAND_PAGE_OOB_SIZE); /* Send command to actually program the data */ nand_command(NAND_CMD_PAGEPROG, -1, -1); /* call wait ready function */ status = nand_wait(FL_WRITING); /* See if device thinks it succeeded */ if (status & 0x01) {// DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); return -EIO; }//#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* The NAND device assumes that it is always writing to * a cleanly erased page. Hence, it performs its internal * write verification only on bits that transitioned from * 1 to 0. The device does NOT verify the whole page on a * byte by byte basis. It is possible that the page was * not completely erased or the page is becoming unusable * due to wear. The read with ECC would catch the error * later when the ECC page check fails, but we would rather * catch it early in the page write stage. Better to write * no data than invalid data. */ /* Send command to read back the page */ nand_command(NAND_CMD_READ0, 0, page); /* Loop through and verify the data */ if (nand_verify_buf(data_poi, NAND_PAGE_SIZE)) {// DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); return -EIO; } /* check, if we have a fs-supplied oob-buffer */ if (oob_buf) { if (nand_verify_buf(oob_data, NAND_PAGE_OOB_SIZE)) {// DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); return -EIO; } } else { // soft ECC used int ecc_bytes = 0; ecc_bytes = (NAND_PAGE_SIZE == 512) ? 6 : 3; nand_read_buf(oob_data, NAND_PAGE_OOB_SIZE); for (i = 0; i < ecc_bytes; i++) { if (oob_data[oob_config[i]] != ecc_code[i]) {// DEBUG (MTD_DEBUG_LEVEL0, "%s: Failed ECC write "// "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); return -EIO; } } } /* * Terminate the read command. This is faster than sending a reset command or * applying a 20us delay before issuing the next programm sequence. * This is not a problem for all chips, but I have found a bunch of them. */ nand_select_chip(-1); nand_select_chip(0);//#endif return 0;}// now, read_ecc(), read_oob(), write_ecc(), and write_oob()/* NAND read with ECC */static int nand_read_ecc(loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel){ int i, j, col, page, end, ecc; int erase_state = 0; int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;// struct nand_chip *this = mtd->priv; u_char *data_poi, *oob_data = oob_buf; u_char ecc_calc[6]; u_char ecc_code[6]; int eccmode; int *oob_config; // use chip default if zero if (oobsel == NULL) oobsel = &oobinfo_buf; // ???? how to initial this /* force ECC_SOFT given by NAND driver, by Jeanson */ eccmode = NAND_ECC_SOFT; //this->eccmode; oob_config = oobsel->eccpos; /* Do not allow reads past end of device */ if ((from + len) > NAND_FLASH_SIZE) {// DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } /* Grab the lock and see if the device is available */ //( FL_READING, &erase_state); // under bootloader, should be ready always /* Select the NAND device */ nand_select_chip(0); /* First we calculate the starting page */ page = from >> ADD2SHIFT; //9, this->page_shift; /* Get raw starting column */ col = from & (NAND_PAGE_SIZE - 1); end = NAND_PAGE_SIZE; //512 bytes ecc = NAND_PAGE_SIZE; //512 bytes /* Send the read command */ nand_command(NAND_CMD_READ0, 0x00, page); /* Pull high SPn, Jeanson */ *(base + NAND_CLR_SPn_REG) = 1; /* Loop until all data read */ while (read < len) { /* If we have consequent page reads, apply delay or wait for ready/busy pin */ if (read) { // wait chip_delay(10) us */ for (i = 0; i < 10; i++) for (j = 0; j < 1000; j++); } /* If the read is not page aligned, we have to read into data buffer * due to ecc, else we read into return buffer direct */ if (!col && (len - read) >= end) data_poi = &buf[read]; else data_poi = oob_databuf; /* get oob area, if we have no oob buffer from fs-driver */ if (!oob_buf) { oob_data = &oob_databuf[end]; oob = 0; } /* Send the read command */ // if this page is the 1st page of current block if ((page % NAND_BLK_PER_PAGE) == 0) // ProChao, 10/13/2003 nand_command(NAND_CMD_READ0, 0x00, page); // j = 0; switch (eccmode) { case NAND_ECC_NONE: /* No ECC, Read in a page */ nand_read_buf(data_poi, end); break; case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ nand_read_buf(data_poi, end); nand_calculate_ecc(&data_poi[0], &ecc_calc[0]); //if (mtd->oobblock == 512) // must be 512 bytes nand_calculate_ecc(&data_poi[256], &ecc_calc[3]); break;#if 0 // commented out by ProChao, 10/8/2003 case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */ this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, data_poi, ecc); this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */ if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */ this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, &data_poi[ecc], end - ecc); this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); /* read from hardware */ } break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page */ this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, data_poi, end); this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */ break;#endif default:// printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);// BUG(); break; } /* Pull low SPn, Jeanson */ *(base + NAND_SET_SPn_REG) = 1; /* read oobdata */ for (j = 0; j < NAND_PAGE_OOB_SIZE; j++) oob_data[oob + j] = nand_read_byte(); // if this page is the 1st page of current block if ((page % NAND_BLK_PER_PAGE) == 0) // ProChao, 10/13/2003 { // yes, this page is the 1st page, using the oob_data to check block validity if (oob_data[5] != SECTOR_FREE) { // this block might be used normal (marked FREE or USED) if ((oob_data[6] != oob_data[7]) || (oob_data[6] != SECTOR_USED) && (oob_data[6] != SECTOR_FREE)) { page += NAND_BLK_PER_PAGE; // move forward to next block col = 0; /* Send the read command */ nand_command(NAND_CMD_READ0, 0x00, page); // continue; } } } /* Skip ECC, if not active */ if (eccmode == NAND_ECC_NONE) goto readdata; /* Pick the ECC bytes out of the oob data */ for (j = 0; j < 6; j++) ecc_code[j] = oob_data[oob + oob_config[j]]; /* correct data, if neccecary */ ecc_status = nand_correct_data(&data_poi[0], &ecc_code[0], &ecc_calc[0]); /* check, if we have a fs supplied oob-buffer */ if (oob_buf) { oob += NAND_PAGE_OOB_SIZE; *((int *) &oob_data[oob]) = ecc_status; oob += sizeof(int); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -