|
@@ -191,7 +191,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
|
|
|
static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
|
|
|
{
|
|
|
struct onenand_chip *this = mtd->priv;
|
|
|
- int value, readcmd = 0;
|
|
|
+ int value, readcmd = 0, block_cmd = 0;
|
|
|
int block, page;
|
|
|
/* Now we use page size operation */
|
|
|
int sectors = 4, count = 4;
|
|
@@ -207,6 +207,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
|
|
|
|
|
|
case ONENAND_CMD_ERASE:
|
|
|
case ONENAND_CMD_BUFFERRAM:
|
|
|
+ case ONENAND_CMD_OTP_ACCESS:
|
|
|
+ block_cmd = 1;
|
|
|
block = (int) (addr >> this->erase_shift);
|
|
|
page = -1;
|
|
|
break;
|
|
@@ -235,7 +237,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
|
|
|
value = onenand_block_address(this, block);
|
|
|
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
|
|
|
|
|
|
- if (cmd == ONENAND_CMD_ERASE) {
|
|
|
+ if (cmd == block_cmd) {
|
|
|
/* Select DataRAM for DDP */
|
|
|
value = onenand_bufferram_address(this, block);
|
|
|
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
|
|
@@ -1412,6 +1414,304 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|
|
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);
|
|
|
+
|
|
|
+ /* 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->oobblock * otp_pages;
|
|
|
+ otp_pages = 64 - otp_pages;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check User/Factory boundary */
|
|
|
+ if (((mtd->oobblock * otp_pages) - (from + len)) < 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ while (len > 0 && otp_pages > 0) {
|
|
|
+ if (!action) { /* OTP Info functions */
|
|
|
+ struct otp_info *otpinfo;
|
|
|
+
|
|
|
+ len -= sizeof(struct otp_info);
|
|
|
+ if (len <= 0)
|
|
|
+ return -ENOSPC;
|
|
|
+
|
|
|
+ otpinfo = (struct otp_info *) buf;
|
|
|
+ otpinfo->start = from;
|
|
|
+ otpinfo->length = mtd->oobblock;
|
|
|
+ otpinfo->locked = 0;
|
|
|
+
|
|
|
+ from += mtd->oobblock;
|
|
|
+ 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 < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ otp_pages--;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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)
|
|
|
+{
|
|
|
+ unsigned char oob_buf[64];
|
|
|
+ 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_print_device_info - Print device ID
|
|
|
* @param device device ID
|
|
@@ -1563,7 +1863,6 @@ static void onenand_resume(struct mtd_info *mtd)
|
|
|
"in suspended state\n");
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* onenand_scan - [OneNAND Interface] Scan for the OneNAND device
|
|
|
* @param mtd MTD device structure
|
|
@@ -1655,6 +1954,14 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
|
|
mtd->write_ecc = onenand_write_ecc;
|
|
|
mtd->read_oob = onenand_read_oob;
|
|
|
mtd->write_oob = onenand_write_oob;
|
|
|
+#ifdef CONFIG_MTD_ONENAND_OTP
|
|
|
+ mtd->get_fact_prot_info = onenand_get_fact_prot_info;
|
|
|
+ mtd->read_fact_prot_reg = onenand_read_fact_prot_reg;
|
|
|
+ mtd->get_user_prot_info = onenand_get_user_prot_info;
|
|
|
+ mtd->read_user_prot_reg = onenand_read_user_prot_reg;
|
|
|
+ mtd->write_user_prot_reg = onenand_write_user_prot_reg;
|
|
|
+ mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
|
|
|
+#endif
|
|
|
mtd->readv = NULL;
|
|
|
mtd->readv_ecc = NULL;
|
|
|
mtd->writev = onenand_writev;
|