📄 onenand_base.c
字号:
this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); if (oob) { oobbuf = this->oob_buf; /* We send data to spare ram with oobsize * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (ops->mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); else memcpy(oobbuf + oobcolumn, oob, thisooblen); oobwritten += thisooblen; oob += thisooblen; oobcolumn = 0; } else oobbuf = (u_char *) ffchars; this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, to, !ret && !subpage); if (ONENAND_IS_2PLANE(this)) { ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); } if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); break; } /* Only check verify write turn on */ ret = onenand_verify(mtd, buf, to, thislen); if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; } written += thislen; if (written == len) break; column = 0; to += thislen; buf += thislen; } ops->retlen = written; return ret;}/** * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write * @param mode operation mode * * OneNAND write out-of-band */static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops){ struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; int written = 0; u_char *oobbuf; size_t len = ops->ooblen; const u_char *buf = ops->oobbuf; mtd_oob_mode_t mode = ops->mode; to += ops->ooboffs; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ ops->oobretlen = 0; if (mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; column = to & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n"); return -EINVAL; } /* For compatibility with NAND: Do not allow write past end of page */ if (unlikely(column + len > oobsize)) { printk(KERN_ERR "onenand_write_oob_nolock: " "Attempt to write past end of page\n"); return -EINVAL; } /* Do not allow reads past end of device */ if (unlikely(to >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (to >> this->page_shift)) * oobsize)) { printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n"); return -EINVAL; } oobbuf = this->oob_buf; /* Loop until all data write */ while (written < len) { int thislen = min_t(int, oobsize, len - written); cond_resched(); this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); /* We send data to spare ram with oobsize * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); else memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); if (ONENAND_IS_2PLANE(this)) { ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, to + this->writesize, 0); } ret = this->wait(mtd, FL_WRITING); if (ret) { printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret); break; } ret = onenand_verify_oob(mtd, oobbuf, to); if (ret) { printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret); break; } written += thislen; if (written == len) break; to += mtd->writesize; buf += thislen; column = 0; } ops->oobretlen = written; return ret;}/** * onenand_write - [MTD Interface] write buffer to FLASH * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write * * Write with ECC */static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf){ struct mtd_oob_ops ops = { .len = len, .ooblen = 0, .datbuf = (u_char *) buf, .oobbuf = NULL, }; int ret; onenand_get_device(mtd, FL_WRITING); ret = onenand_write_ops_nolock(mtd, to, &ops); onenand_release_device(mtd); *retlen = ops.retlen; return ret;}/** * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band * @param mtd: MTD device structure * @param to: offset to write * @param ops: oob operation description structure */static int onenand_write_oob(struct mtd_info *mtd, loff_t to, 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_WRITING); if (ops->datbuf) ret = onenand_write_ops_nolock(mtd, to, ops); else ret = onenand_write_oob_nolock(mtd, to, ops); onenand_release_device(mtd); return ret;}/** * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad * @param mtd MTD device structure * @param ofs offset from device start * @param allowbbt 1, if its allowed to access the bbt area * * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt){ struct onenand_chip *this = mtd->priv; struct bbm_info *bbm = this->bbm; /* Return info from the table */ return bbm->isbad_bbt(mtd, ofs, allowbbt);}/** * onenand_erase - [MTD Interface] erase block(s) * @param mtd MTD device structure * @param instr erase instruction * * Erase one ore more blocks */static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr){ struct onenand_chip *this = mtd->priv; unsigned int block_size; loff_t addr; int len; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); block_size = (1 << this->erase_shift); /* Start address must align on block boundary */ if (unlikely(instr->addr & (block_size - 1))) { printk(KERN_ERR "onenand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (unlikely(instr->len & (block_size - 1))) { printk(KERN_ERR "onenand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if (unlikely((instr->len + instr->addr) > mtd->size)) { printk(KERN_ERR "onenand_erase: Erase past end of device\n"); return -EINVAL; } instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_ERASING); /* Loop throught the pages */ len = instr->len; addr = instr->addr; instr->state = MTD_ERASING; while (len) { cond_resched(); /* Check if we have a bad block, we do not erase bad blocks */ if (onenand_block_isbad_nolock(mtd, addr, 0)) { printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr); instr->state = MTD_ERASE_FAILED; goto erase_exit; } this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); onenand_invalidate_bufferram(mtd, addr, block_size); ret = this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; } len -= block_size; addr += block_size; } instr->state = MTD_ERASE_DONE;erase_exit: ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); /* Do call back function */ if (!ret) mtd_erase_callback(instr); return ret;}/** * onenand_sync - [MTD Interface] sync * @param mtd MTD device structure * * Sync is actually a wait for chip ready function */static void onenand_sync(struct mtd_info *mtd){ DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_SYNCING); /* Release it and go back */ onenand_release_device(mtd);}/** * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad * @param mtd MTD device structure * @param ofs offset relative to mtd start * * Check whether the block is bad */static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs){ int ret; /* Check for invalid offset */ if (ofs > mtd->size) return -EINVAL; onenand_get_device(mtd, FL_READING); ret = onenand_block_isbad_nolock(mtd, ofs, 0); onenand_release_device(mtd); return ret;}/** * onenand_default_block_markbad - [DEFAULT] mark a block bad * @param mtd MTD device structure * @param ofs offset from device start * * This is the default implementation, which can be overridden by * a hardware specific driver. */static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs){ struct onenand_chip *this = mtd->priv; struct bbm_info *bbm = this->bbm; u_char buf[2] = {0, 0}; struct mtd_oob_ops ops = { .mode = MTD_OOB_PLACE, .ooblen = 2, .oobbuf = buf, .ooboffs = 0, }; int block; /* Get block number */ block = ((int) ofs) >> bbm->bbt_erase_shift; if (bbm->bbt) bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); return onenand_write_oob_nolock(mtd, ofs, &ops);}/** * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad * @param mtd MTD device structure * @param ofs offset relative to mtd start * * Mark the block as bad */static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs){ struct onenand_chip *this = mtd->priv; int ret; ret = onenand_block_isbad(mtd, ofs); if (ret) { /* If it was bad already, return success and do nothing */ if (ret > 0) return 0; return ret; } onenand_get_device(mtd, FL_WRITING); ret = this->block_markbad(mtd, ofs); onenand_release_device(mtd); return ret;}/** * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to lock or unlock * @param cmd lock or unlock command * * Lock or unlock one or more blocks */static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd){ struct onenand_chip *this = mtd->priv; int start, end, block, value, status; int wp_status_mask; start = ofs >> this->erase_shift; end = len >> this->erase_shift; if (cmd == ONENAND_CMD_LOCK) wp_status_mask = ONENAND_WP_LS; else wp_status_mask = ONENAND_WP_US; /* Continuous lock scheme */ if (this->options & ONENAND_HAS_CONT_LOCK) { /* Set start block address */ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write lock command */ this->command(mtd, cmd, 0, 0); /* There's no return value */ this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) & ONENAND_CTRL_ONGO) continue; /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & wp_status_mask)) printk(KERN_ERR "wp status = 0x%x\n", status); return 0; } /* Block lock scheme */ for (block = start; block < start + end; block++) { /* Set block address */ value = onenand_block_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); /* Select DataRAM for DDP */ value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); /* Set start block address */ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write lock command */ this->command(mtd, cmd, 0, 0); /* There's no return value */ this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) & ONENAND_CTRL_ONGO) continue; /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -