📄 onenand_base.c
字号:
*retlen = written; return ret;}/** * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band * @mtd: MTD device structure * @from: offset to read from * @ops: oob operation description structure */static int onenand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops){ BUG_ON(ops->mode != MTD_OOB_PLACE); return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->len, &ops->retlen, ops->oobbuf);}/** * 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 onenand_do_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_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 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 < 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 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;}/** * onenand_check_lock_status - [OneNAND Interface] Check lock status * @param this onenand chip data structure * * Check lock status */static void onenand_check_lock_status(struct onenand_chip *this){ unsigned int value, block, status; unsigned int end; end = this->chipsize >> this->erase_shift; for (block = 0; 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); /* 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); }}/** * onenand_unlock_all - [OneNAND Interface] unlock all blocks * @param mtd MTD device structure * * Unlock all blocks */static int onenand_unlock_all(struct mtd_info *mtd){ struct onenand_chip *this = mtd->priv; if (this->options & ONENAND_HAS_UNLOCK_ALL) { /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 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; /* Workaround for all block unlock in DDP */ if (this->device_id & ONENAND_DEVICE_IS_DDP) { loff_t ofs; size_t len; /* 1st block on another chip */ ofs = this->chipsize >> 1; len = 1 << this->erase_shift; onenand_unlock(mtd, ofs, len); } onenand_check_lock_status(this); return 0; } mtd->unlock(mtd, 0x0, this->chipsize); 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->writesize) { memcpy(this->page_buf, buf, len); memset(this->page_buf + len, 0xff, mtd->writesize - len); pbuf = this->page_buf; len = mtd->writesize; } /* 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 = onenand_do_write_oob(mtd, from, len, retlen, buf); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); this->wait(mtd, FL_RESETING); return ret;}/** * onenand_otp_walk - [DEFAULT] Handle OTP operation * @param mtd MTD device structure * @param from The offset to read/write * @param len number of bytes to read/write * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put/get data * @param action do given action * @param mode specify user and factory * * Handle OTP operation. */static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, otp_op_t action, int mode){ struct onenand_chip *this = mtd->priv; int otp_pages; int density; int ret = 0; *retlen = 0; density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT; if (density < ONENAND_DEVICE_DENSITY_512Mb) otp_pages = 20; else otp_pages = 10; if (mode == MTD_OTP_FACTORY) { from += mtd->writesize * otp_pages; otp_pages = 64 - otp_pages; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -