📄 onenand_base.c
字号:
out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); *retlen = written; return ret;}/** * onenand_writev_ecc - [MTD Interface] write with iovec with ecc * @param mtd MTD device structure * @param vecs the iovectors to write * @param count number of vectors * @param to offset to write to * @param retlen pointer to variable to store the number of written bytes * @param eccbuf filesystem supplied oob data buffer * @param oobsel oob selection structure * * OneNAND write with iovec with ecc */static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel){ struct onenand_chip *this = mtd->priv; unsigned char *pbuf; size_t total_len, len; int i, written = 0; int ret = 0; /* Preset written len for early exit */ *retlen = 0; /* Calculate total length of data */ total_len = 0; for (i = 0; i < count; i++) total_len += vecs[i].iov_len; DEBUG(MTD_DEBUG_LEVEL3, "onenand_writev_ecc: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); /* Do not allow write past end of the device */ if (unlikely((to + total_len) > mtd->size)) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempted write past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(total_len))) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempt to write not page aligned data\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_WRITING); /* TODO handling oob */ /* Loop until all keve's data has been written */ len = 0; while (count) { pbuf = this->page_buf; /* * If the given tuple is >= pagesize then * write it out from the iov */ if ((vecs->iov_len - len) >= mtd->oobblock) { pbuf = vecs->iov_base + len; len += mtd->oobblock; /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { vecs++; len = 0; count--; } } else { int cnt = 0, thislen; while (cnt < mtd->oobblock) { thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len); memcpy(this->page_buf + cnt, vecs->iov_base + len, thislen); cnt += thislen; len += thislen; /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { vecs++; len = 0; count--; } } } this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); this->write_bufferram(mtd, ONENAND_DATARAM, pbuf, 0, mtd->oobblock); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); onenand_update_bufferram(mtd, to, 1); ret = this->wait(mtd, FL_WRITING); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: write failed %d\n", ret); goto out; } /* Only check verify write turn on */ ret = onenand_verify_page(mtd, (u_char *) pbuf, to); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: verify failed %d\n", ret); goto out; } written += mtd->oobblock; to += mtd->oobblock; }out: /* Deselect and wakt up anyone waiting on the device */ onenand_release_device(mtd); *retlen = written; return 0;}/** * onenand_writev - [MTD Interface] compabilty function for onenand_writev_ecc * @param mtd MTD device structure * @param vecs the iovectors to write * @param count number of vectors * @param to offset to write to * @param retlen pointer to variable to store the number of written bytes * * OneNAND write with kvec. This just calls the ecc function */static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen){ return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL);}/** * onenand_block_checkbad - [GENERIC] Check if a block is marked bad * @param mtd MTD device structure * @param ofs offset from device start * @param getchip 0, if the chip is already selected * @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_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, 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))) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (unlikely(instr->len & (block_size - 1))) { DEBUG(MTD_DEBUG_LEVEL0, "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)) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); return -EINVAL; } instr->fail_addr = 0xffffffff; /* 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) { /* Check if we have a bad block, we do not erase bad blocks */ if (onenand_block_checkbad(mtd, addr, 0, 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); ret = this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { if (ret == -EPERM) DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Device is write protected!!!\n"); else DEBUG(MTD_DEBUG_LEVEL0, "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; /* Do call back function */ if (!ret) mtd_erase_callback(instr); /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); 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){ /* Check for invalid offset */ if (ofs > mtd->size) return -EINVAL; return onenand_block_checkbad(mtd, ofs, 1, 0);}/** * 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}; size_t retlen; 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 mtd->write_oob(mtd, ofs , 2, &retlen, buf);}/** * 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; } return this->block_markbad(mtd, ofs);}/** * onenand_unlock - [MTD Interface] Unlock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to unlock * * Unlock one or more blocks */static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len){ struct onenand_chip *this = mtd->priv; int start, end, block, value, status; start = ofs >> this->erase_shift; end = len >> this->erase_shift; /* Continuous lock scheme */ if (this->options & ONENAND_CONT_LOCK) { /* Set start block address */ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); /* There's no return value */ this->wait(mtd, FL_UNLOCKING); /* 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 & ONENAND_WP_US)) printk(KERN_ERR "wp status = 0x%x\n", status); return 0; } /* Block lock scheme */ for (block = start; block < 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 unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); /* There's no return value */ this->wait(mtd, FL_UNLOCKING); /* 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 & ONENAND_WP_US)) printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); } return 0;}#ifdef CONFIG_MTD_ONENAND_OTP/* Interal OTP operation */typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, size_t *retlen, u_char *buf);/** * do_otp_read - [DEFAULT] Read OTP block area * @param mtd MTD device structure * @param from The offset to read * @param len number of bytes to read * @param retlen pointer to variable to store the number of readbytes * @param buf the databuffer to put/get data * * Read OTP block area. */static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ struct onenand_chip *this = mtd->priv; int ret; /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); ret = mtd->read(mtd, from, len, retlen, buf); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); this->wait(mtd, FL_RESETING); return ret;}/** * do_otp_write - [DEFAULT] Write OTP block area * @param mtd MTD device structure * @param from The offset to write * @param len number of bytes to write * @param retlen pointer to variable to store the number of write bytes * @param buf the databuffer to put/get data * * Write OTP block area. */static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ struct onenand_chip *this = mtd->priv; unsigned char *pbuf = buf; int ret; /* Force buffer page aligned */ if (len < mtd->oobblock) { memcpy(this->page_buf, buf, len); memset(this->page_buf + len, 0xff, mtd->oobblock - len); pbuf = this->page_buf; len = mtd->oobblock; } /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); ret = mtd->write(mtd, from, len, retlen, pbuf); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); this->wait(mtd, FL_RESETING); return ret;}/** * do_otp_lock - [DEFAULT] Lock OTP block area * @param mtd MTD device structure * @param from The offset to lock * @param len number of bytes to lock * @param retlen pointer to variable to store the number of lock bytes * @param buf the databuffer to put/get data * * Lock OTP block area. */static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ struct onenand_chip *this = mtd->priv; int ret; /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); ret = mtd->write_oob(mtd, from, len, retlen, buf);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -