📄 onenand_base.c
字号:
if (!(status & wp_status_mask)) printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); } return 0;}/** * onenand_lock - [MTD Interface] Lock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to unlock * * Lock one or more blocks */static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len){ int ret; onenand_get_device(mtd, FL_LOCKING); ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); onenand_release_device(mtd); return ret;}/** * 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){ int ret; onenand_get_device(mtd, FL_LOCKING); ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); onenand_release_device(mtd); return ret;}/** * onenand_check_lock_status - [OneNAND Interface] Check lock status * @param this onenand chip data structure * * Check lock status */static int 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); return 0; } } return 1;}/** * onenand_unlock_all - [OneNAND Interface] unlock all blocks * @param mtd MTD device structure * * Unlock all blocks */static void onenand_unlock_all(struct mtd_info *mtd){ struct onenand_chip *this = mtd->priv; loff_t ofs = 0; size_t len = this->chipsize; if (this->options & ONENAND_HAS_UNLOCK_ALL) { /* Set start block address */ this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 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 */ if (onenand_check_lock_status(this)) return; /* Workaround for all block unlock in DDP */ if (ONENAND_IS_DDP(this)) { /* All blocks on another chip */ ofs = this->chipsize >> 1; len = this->chipsize >> 1; } } onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);}#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; struct mtd_oob_ops ops = { .len = len, .ooblen = 0, .datbuf = buf, .oobbuf = NULL, }; int ret; /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); ret = onenand_read_ops_nolock(mtd, from, &ops); /* 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 to 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 to, size_t len, size_t *retlen, u_char *buf){ struct onenand_chip *this = mtd->priv; unsigned char *pbuf = buf; int ret; struct mtd_oob_ops ops; /* 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); ops.len = len; ops.ooblen = 0; ops.datbuf = pbuf; ops.oobbuf = NULL; ret = onenand_write_ops_nolock(mtd, to, &ops); *retlen = ops.retlen; /* 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; struct mtd_oob_ops ops = { .mode = MTD_OOB_PLACE, .ooblen = len, .oobbuf = buf, .ooboffs = 0, }; int ret; /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); ret = onenand_write_oob_nolock(mtd, from, &ops); *retlen = ops.oobretlen; /* 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 = onenand_get_density(this->device_id); 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; } /* Check User/Factory boundary */ if (((mtd->writesize * otp_pages) - (from + len)) < 0) return 0; onenand_get_device(mtd, FL_OTPING); while (len > 0 && otp_pages > 0) { if (!action) { /* OTP Info functions */ struct otp_info *otpinfo; len -= sizeof(struct otp_info); if (len <= 0) { ret = -ENOSPC; break; } otpinfo = (struct otp_info *) buf; otpinfo->start = from; otpinfo->length = mtd->writesize; otpinfo->locked = 0; from += mtd->writesize; buf += sizeof(struct otp_info); *retlen += sizeof(struct otp_info); } else { size_t tmp_retlen; int size = len; ret = action(mtd, from, len, &tmp_retlen, buf); buf += size; len -= size; *retlen += size; if (ret) break; } otp_pages--; } onenand_release_device(mtd); return ret;}/** * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info * @param mtd MTD device structure * @param buf the databuffer to put/get data * @param len number of bytes to read * * Read factory OTP info. */static int onenand_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, size_t len){ size_t retlen; int ret; ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY); return ret ? : retlen;}/** * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP 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 read bytes * @param buf the databuffer to put/get data * * Read factory OTP area. */static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY);}/** * onenand_get_user_prot_info - [MTD Interface] Read user OTP info * @param mtd MTD device structure * @param buf the databuffer to put/get data * @param len number of bytes to read * * Read user OTP info. */static int onenand_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf, size_t len){ size_t retlen; int ret; ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER); return ret ? : retlen;}/** * onenand_read_user_prot_reg - [MTD Interface] Read user OTP 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 read bytes * @param buf the databuffer to put/get data * * Read user OTP area. */static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER);}/** * onenand_write_user_prot_reg - [MTD Interface] Write user OTP 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 user OTP area. */static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER);}/** * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area * @param mtd MTD device structure * @param from The offset to lock * @param len number of bytes to unlock * * Write lock mark on spare area in page 0 in OTP block */static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len){ struct onenand_chip *this = mtd->priv; u_char *oob_buf = this->oob_buf; size_t retlen; int ret; memset(oob_buf, 0xff, mtd->oobsize); /* * Note: OTP lock operation * OTP block : 0xXXFC * 1st block : 0xXXF3 (If chip support) * Both : 0xXXF0 (If chip support) */ oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; /* * Write lock mark to 8th word of sector0 of page0 of the spare0. * We write 16 bytes spare area instead of 2 bytes. */ from = 0; len = 16; ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER); return ret ? : retlen;}#endif /* CONFIG_MTD_ONENAND_OTP *//** * onenand_check_features - Check and set OneNAND features * @param mtd MTD data structure * * Check and set OneNAND features * - lock scheme * - two plane */static void onenand_check_features(struct mtd_info *mtd){ struct onenand_chip *this = mtd->priv; unsigned int density, process; /* Lock scheme depends on density and process */ density = onenand_get_density(this->device_id); process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; /* Lock scheme */ switch (density) { case ONENAND_DEVICE_DENSITY_4Gb: this->options |= ONENAND_HAS_2PLANE; case ONENAND_DEVICE_DENSITY_2Gb: /* 2Gb DDP don't have 2 plane */ if (!ONENAND_IS_DDP(this)) this->options |= ONENAND_HAS_2PLANE; this->options |= ONENAND_HAS_UNLOCK_ALL; case ONENAND_DEVICE_DENSITY_1Gb: /* A-Die has all block unlock */ if (process) this->options |= ONENAND_HAS_UNLOCK_ALL; break; default: /* Some OneNAND has continuous lock scheme */ if (!process) this->options |= ONENAND_HAS_CONT_LOCK; break; } if (this->options & ONENAND_HAS_CONT_LOCK) printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); if (this->options & ONENAND_HAS_UNLOCK_ALL) printk(KERN_DEBUG "Chip support all block unlock\n"); if (this->options & ONENAND_HAS_2PLANE) printk(KERN_DEBUG "Chip has 2 plane\n");}/** * onenand_print_device_info - Print device & version ID * @param device device ID * @param version version ID * * Print device & version ID */static void onenand_print_device_info(int device, int version){ int vcc, demuxed, ddp, density; vcc = device & ONENAND_DE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -