📄 nand.c
字号:
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 */ this->select_chip(mtd, -1); /* Wake up anyone waiting on the device */ this->state = FL_READY; /* * 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; } /* Select the NAND device */ this->select_chip(mtd, 0); /* 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 ! */ i = 0; while (i < len) { int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1); if (!thislen) thislen = mtd->oobsize; if(len<thislen) thislen=(int)len; this->read_buf(mtd, &buf[i], thislen); i += thislen; col += thislen; /* Delay between pages */ udelay (this->chip_delay); } /* De-select the NAND device */ this->select_chip(mtd, -1); /* 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 i, page, col, cnt, 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; /* Get the starting column */ col = to & (mtd->oobblock - 1); /* Select the NAND device */ this->select_chip(mtd, 0); /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); while(!this->dev_ready(mtd)) udelay (this->chip_delay); if (!(this->read_byte(mtd) & 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) { /* * Check, if we have a full page write, then we can * use the given buffer, else we have to copy */ if (!col && (len - written) >= mtd->oobblock) { this->data_poi = (u_char*) &buf[written]; cnt = mtd->oobblock; } else { cnt = 0; for (i = col; i < len && i < mtd->oobblock; i++) { this->data_buf[i] = buf[written + i]; cnt++; } this->data_poi = this->data_buf; } /* We use the same function for write and writev */ if (eccbuf) { ret = nand_write_page (mtd, this, page, col, cnt ,&eccbuf[oob], oobsel); oob += mtd->oobsize; } else ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel); if (ret) goto out; /* Update written bytes count */ written += cnt; col = 0; /* Increment page address */ page++; }out: /* De-select the NAND device */ this->select_chip(mtd, -1); *retlen = written; return ret;}static u_char ffchars[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};/* * 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 column, page, status, ret = 0; struct nand_chip *this = mtd->priv;#ifdef CONFIG_MTD_NAND_VERIFY_WRITE int i;#endif 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; } /* Select the NAND device */ this->select_chip(mtd, 0); /* Reset the chip. Some chips (like the Toshiba TC5832DC found in one of my DiskOnChip 2000 test units) will clear the whole data page too if we don't do this. I have no clue why, but I seem to have 'fixed' it in the doc2000 driver in August 1999. dwmw2. */ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); while(!this->dev_ready(mtd)) udelay (this->chip_delay); if (!(this->read_byte(mtd) & 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); this->hwcontrol(mtd,NAND_CTL_DAT_OUT); /* prepad 0xff for partial programming */ this->write_buf(mtd, ffchars, column); /* write data */ this->write_buf(mtd, buf, len); /* postpad 0xff for partial programming */ this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); this->hwcontrol(mtd,NAND_CTL_DAT_IN); /* Send command to program the OOB data */ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); while(!this->dev_ready(mtd)) udelay (this->chip_delay); status = this->waitfunc (mtd, this, FL_WRITING); /* See if device thinks it succeeded */ if (status & 0x01) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); ret = -EIO; goto out; } /* Return happy */ *retlen = len;#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* Send command to read back the data */ this->cmdfunc (mtd, NAND_CMD_READOOB, column, page); /* Loop through and verify the data */ for (i = 0; i < len; i++) { if (buf[i] != this->read_byte(mtd)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); ret = -EIO; goto out; } }#endifout: /* De-select the NAND device */ this->select_chip(mtd, -1); return ret;}/* * NAND erase a block */static int nand_erase (struct mtd_info *mtd, struct erase_info *instr){ int i,page, len, status, pages_per_block; struct nand_chip *this = mtd->priv; DEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); /* Start address must align on block boundary */ if (instr->addr & (mtd->erasesize - 1)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & (mtd->erasesize - 1)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if ((instr->len + instr->addr) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); return -EINVAL; } /* Shift to get first page */ page = (int) (instr->addr >> this->page_shift); /* Calculate pages in each block */ pages_per_block = mtd->erasesize / mtd->oobblock; /* Select the NAND device */ this->select_chip(mtd, 0); /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); while(!this->dev_ready(mtd)) udelay (this->chip_delay); if (!(this->read_byte(mtd) & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); this->select_chip(mtd, -1); return -EIO; } /* Loop through the pages */ len = instr->len; instr->state = MTD_ERASING; while (len) { /* Send commands to erase a page */ this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); while(!this->dev_ready(mtd)) udelay(this->chip_delay); // spin_unlock_bh (&this->chip_lock); status = this->waitfunc (mtd, this, FL_ERASING); for(i=0;i<32;i++){ udelay(125); this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); while(!this->dev_ready(mtd)) udelay(this->chip_delay); status=this->read_byte(mtd); if(status & 0x40) break; } /* See if block erase succeeded */ if (status & 0x01) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);// instr->state = MTD_ERASE_FAILED; return -EIO; } /* Increment page address and decrement length */ len -= mtd->erasesize; page += pages_per_block; } /* De-select the NAND device */ this->select_chip(mtd, -1); /* Return more or less happy */ return 0;}/* * Scan for the NAND device */int nand_scan (struct mtd_info *mtd, int maxchips){ int i, nand_maf_id, nand_dev_id; struct nand_chip *this = mtd->priv; /* check for proper chip_delay setup, set 20us if not */ if (!this->chip_delay) this->chip_delay = 20; /* check, if a user supplied command function given */ if (this->cmdfunc == NULL) this->cmdfunc = nand_command; if (this->waitfunc == NULL) this->waitfunc = nand_wait; if (!this->block_bad) this->block_bad = nand_block_bad; if (!this->select_chip) this->select_chip = nand_select_chip;// if (!this->write_byte)// this->write_byte = nand_write_byte;// if (!this->read_byte)// this->read_byte = nand_read_byte; if (!this->write_buf) this->write_buf = nand_write_buf; if (!this->read_buf) this->read_buf = nand_read_buf; if (!this->verify_buf) this->verify_buf = nand_verify_buf; /* Select the device */ this->select_chip(mtd, 0);// this->hwcontrol(mtd,NAND_CTL_CLRRnB); while(!this->dev_ready(mtd)) udelay (this->chip_delay); /* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); for (i = 0; i < 100;i++); /* Read manufacturer and device IDs */ nand_maf_id = this->read_byte(mtd);// nand_maf_id = (u_char)NFDATA; nand_dev_id = this->read_byte(mtd);// nand_dev_id = (u_char)NFDATA; /* Print and store flash device information */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) { mtd->name = nand_flash_ids[i].name; mtd->erasesize = nand_flash_ids[i].erasesize; mtd->eccsize = 256; this->chipshift = nand_flash_ids[i].chipshift; if (nand_flash_ids[i].page256) { mtd->oobblock = 256; mtd->oobsize = 8; this->page_shift = 8; } else { mtd->oobblock = 512; mtd->oobsize = 16; this->page_shift = 9; } /* Try to identify manufacturer */ for (i = 0; nand_manuf_ids[i].id != 0x0; i++) { if (nand_manuf_ids[i].id == nand_maf_id) break; } printk("NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, nand_manuf_ids[i].name , mtd->name); break; } } if (!mtd->name) { printk ("No NAND device found!!!\n"); return 1; } for (i=1; i < maxchips; i++) { this->select_chip(mtd, i); /* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ if (nand_maf_id != this->read_byte(mtd) ||nand_dev_id != this->read_byte(mtd))// if (nand_maf_id !=(u_char)NFDATA||nand_dev_id !=(u_char)NFDATA) break; } if (i > 1) printk("%d NAND chips detected\n", i); mtd->size = (1 << this->chipshift) /* * i when we fix the rest of the code */; /* * check ECC mode, default to software * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize * fallback to software ECC */ this->eccsize = 256; /* set default eccsize */ switch (this->eccmode) { case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: if (mtd->oobblock == 256) { printk ("512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; break; } else this->eccsize = 512; /* set eccsize to 512 and fall through for function check */ case NAND_ECC_HW3_256: if (this->calculate_ecc && this->correct_data && this->enable_hwecc) break; printk ("No ECC functions supplied, Hardware ECC not possible\n"); return -1; case NAND_ECC_NONE: printk ("NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); this->eccmode = NAND_ECC_NONE; break; case NAND_ECC_SOFT: this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; break; default: printk ("Invalid NAND_ECC_MODE %d\n", this->eccmode); return -1; } /* Initialize state, waitqueue and spinlock */ this->state = FL_READY; /* De-select the device */ this->select_chip(mtd, -1); /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; mtd->ecctype = MTD_ECC_SW; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; mtd->read = nand_read; mtd->write = nand_write; mtd->read_ecc = nand_read_ecc; mtd->write_ecc = nand_write_ecc; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob;// mtd->readv = NULL;// mtd->writev = nand_writev;// mtd->writev_ecc = nand_writev_ecc;// mtd->sync = nand_sync; mtd->lock = NULL; mtd->unlock = NULL; /* Return happy */ return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -