📄 onenand_base.c
字号:
*/static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, unsigned int len){ struct onenand_chip *this = mtd->priv; int i; loff_t end_addr = addr + len; /* Invalidate BufferRAM */ for (i = 0; i < MAX_BUFFERRAM; i++) { loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; if (buf_addr >= addr && buf_addr < end_addr) this->bufferram[i].blockpage = -1; }}/** * onenand_get_device - [GENERIC] Get chip for selected access * @param mtd MTD device structure * @param new_state the state which is requested * * Get the device and lock it for exclusive access */static void onenand_get_device(struct mtd_info *mtd, int new_state){ /* Do nothing */}/** * onenand_release_device - [GENERIC] release chip * @param mtd MTD device structure * * Deselect, release chip lock and wake up anyone waiting on the device */static void onenand_release_device(struct mtd_info *mtd){ /* Do nothing */}/** * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer * @param mtd MTD device structure * @param buf destination address * @param column oob offset to read from * @param thislen oob length to read */static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, int thislen){ struct onenand_chip *this = mtd->priv; struct nand_oobfree *free; int readcol = column; int readend = column + thislen; int lastgap = 0; unsigned int i; uint8_t *oob_buf = this->oob_buf; free = this->ecclayout->oobfree; for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { if (readcol >= lastgap) readcol += free->offset - lastgap; if (readend >= lastgap) readend += free->offset - lastgap; lastgap = free->offset + free->length; } this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); free = this->ecclayout->oobfree; for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { int free_end = free->offset + free->length; if (free->offset < readend && free_end > readcol) { int st = max_t(int,free->offset,readcol); int ed = min_t(int,free_end,readend); int n = ed - st; memcpy(buf, oob_buf + st, n); buf += n; } else if (column == 0) break; } return 0;}/** * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * @param mtd MTD device structure * @param from offset to read from * @param ops oob operation description structure * * OneNAND read main and/or out-of-band data */static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ struct onenand_chip *this = mtd->priv; struct mtd_ecc_stats stats; size_t len = ops->len; size_t ooblen = ops->ooblen; u_char *buf = ops->datbuf; u_char *oobbuf = ops->oobbuf; int read = 0, column, thislen; int oobread = 0, oobcolumn, thisooblen, oobsize; int ret = 0, boundary = 0; int writesize = this->writesize; MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; oobcolumn = from & (mtd->oobsize - 1); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n"); ops->retlen = 0; ops->oobretlen = 0; return -EINVAL; } stats = mtd->ecc_stats; /* Read-while-load method */ /* Do first load to bufferRAM */ if (read < len) { if (!onenand_check_bufferram(mtd, from)) { this->main_buf = buf; this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); if (ret == -EBADMSG) ret = 0; } } thislen = min_t(int, writesize, len - read); column = from & (writesize - 1); if (column + thislen > writesize) thislen = writesize - column; while (!ret) { /* If there is more to load then start next load */ from += thislen; if (read + thislen < len) { this->main_buf = buf + thislen; this->command(mtd, ONENAND_CMD_READ, from, writesize); /* * Chip boundary handling in DDP * Now we issued chip 1 read and pointed chip 1 * bufferam so we have to point chip 0 bufferam. */ if (ONENAND_IS_DDP(this) && unlikely(from == (this->chipsize >> 1))) { this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); boundary = 1; } else boundary = 0; ONENAND_SET_PREV_BUFFERRAM(this); } /* While load is going, read from last bufferRAM */ this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen); /* Read oob area if needed */ if (oobbuf) { thisooblen = oobsize - oobcolumn; thisooblen = min_t(int, thisooblen, ooblen - oobread); if (ops->mode == MTD_OOB_AUTO) onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); else this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); oobread += thisooblen; oobbuf += thisooblen; oobcolumn = 0; } /* See if we are done */ read += thislen; if (read == len) break; /* Set up for next read from bufferRAM */ if (unlikely(boundary)) this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; thislen = min_t(int, writesize, len - read); column = 0; /* Now wait for load */ ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); if (ret == -EBADMSG) ret = 0; } /* * Return success, if no ECC failures, else -EBADMSG * fs driver will take care of that, because * retlen == desired len and result == -EBADMSG */ ops->retlen = read; ops->oobretlen = oobread; if (ret) return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;}/** * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param ops oob operation description structure * * OneNAND read out-of-band data from the spare area */static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ struct onenand_chip *this = mtd->priv; struct mtd_ecc_stats stats; int read = 0, thislen, column, oobsize; size_t len = ops->ooblen; mtd_oob_mode_t mode = ops->mode; u_char *buf = ops->oobbuf; int ret = 0; from += ops->ooboffs; MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Initialize return length value */ ops->oobretlen = 0; if (mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; column = from & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n"); return -EINVAL; } /* Do not allow reads past end of device */ if (unlikely(from >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (from >> this->page_shift)) * oobsize)) { printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n"); return -EINVAL; } stats = mtd->ecc_stats; while (read < len) { thislen = oobsize - column; thislen = min_t(int, thislen, len); this->spare_buf = buf; this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = this->wait(mtd, FL_READING); if (ret && ret != -EBADMSG) { printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); break; } if (mode == MTD_OOB_AUTO) onenand_transfer_auto_oob(mtd, buf, column, thislen); else this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); read += thislen; if (read == len) break; buf += thislen; /* Read more? */ if (read < len) { /* Page size */ from += mtd->writesize; column = 0; } } ops->oobretlen = read; if (ret) return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; return 0;}/** * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data * * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL*/int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){ struct mtd_oob_ops ops = { .len = len, .ooblen = 0, .datbuf = buf, .oobbuf = NULL, }; int ret; onenand_get_device(mtd, FL_READING); ret = onenand_read_ops_nolock(mtd, from, &ops); onenand_release_device(mtd); *retlen = ops.retlen; return ret;}/** * onenand_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param ops oob operations description structure * * OneNAND main and/or out-of-band */int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ int ret; switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: break; case MTD_OOB_RAW: /* Not implemented yet */ default: return -EINVAL; } onenand_get_device(mtd, FL_READING); if (ops->datbuf) ret = onenand_read_ops_nolock(mtd, from, ops); else ret = onenand_read_oob_nolock(mtd, from, ops); onenand_release_device(mtd); return ret;}/** * onenand_bbt_wait - [DEFAULT] wait until the command is done * @param mtd MTD device structure * @param state state to select the max. timeout value * * Wait for command done. */static int onenand_bbt_wait(struct mtd_info *mtd, int state){ struct onenand_chip *this = mtd->priv; unsigned int flags = ONENAND_INT_MASTER; unsigned int interrupt; unsigned int ctrl; while (1) { interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); if (interrupt & flags) break; } /* To get correct interrupt status in timeout case */ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); if (interrupt & ONENAND_INT_READ) { int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc & ONENAND_ECC_2BIT_ALL) return ONENAND_BBT_READ_ERROR; } else { printk(KERN_ERR "onenand_bbt_wait: read timeout!" "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); return ONENAND_BBT_READ_FATAL_ERROR; } /* Initial bad block case: 0x2400 or 0x0400 */ if (ctrl & ONENAND_CTRL_ERROR) { printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); return ONENAND_BBT_READ_ERROR; } return 0;}/** * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan * @param mtd MTD device structure * @param from offset to read from * @param ops oob operation description structure * * OneNAND read out-of-band data from the spare area for bbt scan */int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; int ret = 0; size_t len = ops->ooblen; u_char *buf = ops->oobbuf; MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); /* Initialize return value */ ops->oobretlen = 0; /* Do not allow reads past end of device */ if (unlikely((from + len) > mtd->size)) { printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); return ONENAND_BBT_READ_FATAL_ERROR; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); column = from & (mtd->oobsize - 1); while (read < len) { thislen = mtd->oobsize - column; thislen = min_t(int, thislen, len); this->spare_buf = buf; this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = this->bbt_wait(mtd, FL_READING); if (ret) break; this->read_spareram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); read += thislen; if (read == len) break; buf += thislen; /* Read more? */ if (read < len) { /* Update Page size */ from += this->writesize; column = 0; } } /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); ops->oobretlen = read; return ret;}#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE/** * onenand_verify_oob - [GENERIC] verify the oob contents after a write * @param mtd MTD device structure * @param buf the databuffer to verify * @param to offset to read from */static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to){ struct onenand_chip *this = mtd->priv; u_char *oob_buf = this->oob_buf; int status, i; this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status = this->wait(mtd, FL_READING); if (status) return status; this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); for (i = 0; i < mtd->oobsize; i++) if (buf[i] != 0xFF && buf[i] != oob_buf[i]) return -EBADMSG; return 0;}/** * onenand_verify - [GENERIC] verify the chip contents after a write * @param mtd MTD device structure * @param buf the databuffer to verify * @param addr offset to read from * @param len number of bytes to read and compare */static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len){ struct onenand_chip *this = mtd->priv; void __iomem *dataram; int ret = 0; int thislen, column; while (len != 0) { thislen = min_t(int, this->writesize, len); column = addr & (this->writesize - 1); if (column + thislen > this->writesize) thislen = this->writesize - column; this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); onenand_update_bufferram(mtd, addr, 0); ret = this->wait(mtd, FL_READING); if (ret) return ret; onenand_update_bufferram(mtd, addr, 1); dataram = this->base + ONENAND_DATARAM; dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); if (memcmp(buf, dataram + column, thislen)) return -EBADMSG; len -= thislen; buf += thislen; addr += thislen; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -