?? nand.c
字號:
}#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 */ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); /* Loop through and verify the data */ for (i = 0; i < mtd->oobblock; i++) {// if (this->data_poi[i] != readb (this->IO_ADDR_R)) { if (this->data_poi[i] != NF_RDDATA()){ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed 1 write verify, page 0x%08x ", __FUNCTION__, page); return -EIO; } } /* check, if we have a fs-supplied oob-buffer */ if (oob_buf) { for (i = 0; i < mtd->oobsize; i++) {// if (oob_data[i] != readb (this->IO_ADDR_R)) { if (oob_data[i] != NF_RDDATA()){ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed 2 write verify, page 0x%08x ", __FUNCTION__, page); return -EIO; } } } else { if (eccmode != NAND_ECC_NONE) { int ecc_bytes = 0; switch (this->eccmode) { case NAND_ECC_SOFT: case NAND_ECC_HW3_256: ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; break; case NAND_ECC_HW3_512: ecc_bytes = 3; break; case NAND_ECC_HW6_512: ecc_bytes = 6; break; } for (i = 0; i < mtd->oobsize; i++)// oob_data[i] = readb (this->IO_ADDR_R); oob_data[i] = NF_RDDATA(); 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; } } } }#endif return 0;}/** Use NAND read ECC*/static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){// printk("nand_read \n"); return (nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL));} /* * NAND read with ECC */static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel){ int 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;/* static unsigned int zym; if(zym != ((unsigned int)from & 0xffff0000)) { zym = ((unsigned int)from & 0xffff0000); printk("nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); }*/ // use chip default if zero if (oobsel == NULL) oobsel = &mtd->oobinfo; eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; if(eccmode == NAND_ECC_NONE) DEBUG (MTD_DEBUG_LEVEL3,"nand_read_ecc: eccmode == NAND_ECC_NONE\n"); oob_config = oobsel->eccpos; DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Do not allow reads past end of device */ if ((from + len) > mtd->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 */ nand_get_chip (this, mtd ,FL_READING, &erase_state); /* Select the NAND device */ nand_select (); /* First we calculate the starting page */ page = from >> this->page_shift; /* Get raw starting column */ col = from & (mtd->oobblock - 1); end = mtd->oobblock; ecc = mtd->eccsize; /* Send the read command */ this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); /* Loop until all data read */ while (read < len) { /* If we have consequent page reads, apply delay or wait for ready/busy pin */ if (read) { if (!this->dev_ready) udelay (this->chip_delay); else while (!this->dev_ready()); } /* * 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 = this->data_buf; /* get oob area, if we have no oob buffer from fs-driver */ if (!oob_buf) { oob_data = &this->data_buf[end]; oob = 0; } j = 0; switch (eccmode) { case NAND_ECC_NONE: /* No ECC, Read in a page */ // printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); while (j < end)// data_poi[j++] = readb (this->IO_ADDR_R); data_poi[j++] = NF_RDDATA(); break; case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ while (j < end)// data_poi[j++] = readb (this->IO_ADDR_R); data_poi[j++] = NF_RDDATA(); this->calculate_ecc (&data_poi[0], &ecc_calc[0]); if (mtd->oobblock == 512) this->calculate_ecc (&data_poi[256], &ecc_calc[3]); break; case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */ this->enable_hwecc (NAND_ECC_READ); while (j < ecc)// data_poi[j++] = readb (this->IO_ADDR_R); data_poi[j++] = NF_RDDATA(); this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */ if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */ this->enable_hwecc (NAND_ECC_READ); while (j < end)// data_poi[j++] = readb (this->IO_ADDR_R); data_poi[j++] = NF_RDDATA(); this->calculate_ecc (&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 (NAND_ECC_READ); while (j < end)// data_poi[j++] = readb (this->IO_ADDR_R); data_poi[j++] = NF_RDDATA(); this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */ break; default: printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); BUG(); } /* read oobdata */ for (j = 0; j < mtd->oobsize; j++) // oob_data[oob + j] = readb (this->IO_ADDR_R); oob_data[oob + j] = NF_RDDATA(); /* 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 = this->correct_data (&data_poi[0], &ecc_code[0], &ecc_calc[0]); /* check, if we have a fs supplied oob-buffer */ if (oob_buf) { oob += mtd->oobsize; *((int *)&oob_data[oob]) = ecc_status; oob += sizeof(int); } if (ecc_status == -1) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); ecc_failed++; } if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) { ecc_status = this->correct_data (&data_poi[256], &ecc_code[3], &ecc_calc[3]); if (oob_buf) { *((int *)&oob_data[oob]) = ecc_status; oob += sizeof(int); } if (ecc_status == -1) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); ecc_failed++; } }readdata: if (col || (len - read) < end) { for (j = col; j < end && read < len; j++) buf[read++] = data_poi[j]; } else read += mtd->oobblock; /* For subsequent reads align to page boundary. */ col = 0; /* Increment page address */ page++; } /* De-select the NAND device */ nand_deselect (); /* Wake up anyone waiting on the device */ spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); /* * Return success, if no ECC failures, else -EIO * fs driver will take care of that, because * retlen == desired len and result == -EIO */ *retlen = read; return ecc_failed ? -EIO : 0;}/* * NAND read out-of-band */static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){ int i, col, page; int erase_state = 0; struct nand_chip *this = mtd->priv; DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Shift to get page */ page = ((int) from) >> this->page_shift; /* Mask to get column */ col = from & 0x0f; /* Initialize return length value */ *retlen = 0; /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_chip (this, mtd , FL_READING, &erase_state); /* Select the NAND device */ nand_select (); /* Send the read command */ this->cmdfunc (mtd, NAND_CMD_READOOB, col, page); /* * Read the data, if we read more than one page * oob data, let the device transfer the data ! */ for (i = 0; i < len; i++) {// buf[i] = readb (this->IO_ADDR_R); buf[i] = NF_RDDATA(); if ((col++ & (mtd->oobsize - 1)) == (mtd->oobsize - 1)) udelay (this->chip_delay); } /* De-select the NAND device */ nand_deselect (); /* Wake up anyone waiting on the device */ spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); /* Return happy */ *retlen = len; return 0;}#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0/** Use NAND write ECC*/static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf){ return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));} /* * NAND write with ECC */static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel){ int page, ret = 0, oob = 0, written = 0; struct nand_chip *this = mtd->priv; DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Do not allow write past end of device */ if ((to + len) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); return -EINVAL; } /* reject writes, which are not page aligned */ if (NOTALIGNED (to) || NOTALIGNED(len)) { printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); return -EINVAL; } // if oobsel is NULL, use chip defaults if (oobsel == NULL) oobsel = &mtd->oobinfo; /* Shift to get page */ page = ((int) to) >> this->page_shift; /* Grab the lock and see if the device is available */ nand_get_chip (this, mtd, FL_WRITING, NULL); /* Select the NAND device */ nand_select (); /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);// if (!(readb (this->IO_ADDR_R) & 0x80)) { if (!(NF_RDDATA() & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n"); ret = -EIO; goto out; } /* Loop until all data is written */ while (written < len) { int cnt = mtd->oobblock; this->data_poi = (u_char*) &buf[written]; /* We use the same function for write and writev */ if (eccbuf) { ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel); oob += mtd->oobsize; } else ret = nand_write_page (mtd, this, page, NULL, oobsel); if (ret) goto out; /* Update written bytes count */ written += cnt; /* Increment page address */ page++; }out: /* De-select the NAND device */ nand_deselect (); /* Wake up anyone waiting on the device */ spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); *retlen = written; return ret;}/* * NAND write out-of-band */static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf){ int i, column, page, status, ret = 0; struct nand_chip *this = mtd->priv; DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Shift to get page */ page = ((int) to) >> this->page_shift; /* Mask to get column */ column = to & 0x1f; /* Initialize return length value */ *retlen = 0; /* Do not allow write past end of page */ if ((column + len) > mtd->oobsize) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_chip (this, mtd, FL_WRITING, NULL); /* Select the NAND device */ nand_select (); /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);// if (!(readb (this->IO_ADDR_R) & 0x80)) { if (!(NF_RDDATA() & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n"); ret = -EIO; goto out; } /* Write out desired data */ this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page); /* prepad 0xff for partial programming */ for (i = 0; i < column; i++)// writeb (0xff, this->IO_ADDR_W); NF_WRDATA(0xff); /* write data */ for (i = 0; i < len; i++)// writeb (buf[i], this->IO_ADDR_W); NF_WRDATA(buf[i]); /* postpad 0xff for partial programming */ for (i = len + column; i < mtd->oobsize; i++)// writeb (0xff, this->IO_ADDR_W); NF_WRDATA(0xff); /* Send command to program the OOB data */ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -