linux-mtd.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] [OneNAND] OTP support re-implementation 1/1
@ 2009-08-17 10:43 Amul Kumar Saha
  2009-08-26  5:41 ` Amul Kumar Saha
  0 siblings, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-08-17 10:43 UTC (permalink / raw)
  To: linux-mtd

Re-implemented OTP support for OneNAND
Added following features to OneNAND
	1. Lock only 1st Block in OneNAND
	2. Lock BOTH 1st Block and OTP Block in OneNAND

Signed-off-by: Amul Kumar Saha <amul.saha@samsung.com>
---
 drivers/mtd/onenand/Kconfig        |   19 ++
 drivers/mtd/onenand/onenand_base.c |  291 +++++++++++++++++++++++++++++++++----
 include/linux/mtd/onenand.h        |    2
 3 files changed, 281 insertions(+), 31 deletions(-)

diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 79fa79e..07ca80c 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -48,6 +48,25 @@ config MTD_ONENAND_OTP

 	  OTP block is fully-guaranteed to be a valid block.

+if MTD_ONENAND_OTP
+
+config ONENAND_OTP_AREA
+	bool "OTP area ONLY"
+	depends on MTD_ONENAND_OTP
+	select ON_OTP_AREA
+
+config ONENAND_OTP_BLOCK0
+	bool "Block[0] ONLY"
+	depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA
+	select ON_OTP_BLOCK0
+
+config ONENAND_OTP_AREA_BLOCK0
+	bool "BOTH OTP area AND Block[0]"
+	depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA && !ONENAND_OTP_BLOCK0
+	select ON_OTP_AREA_BLOCK0
+
+endif #MTD_ONENAND_OTP
+
 config MTD_ONENAND_2X_PROGRAM
 	bool "OneNAND 2X program support"
 	help
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 6e82909..3da9d63 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -13,6 +13,10 @@
  *	Flex-OneNAND support
  *	Copyright (C) Samsung Electronics, 2008
  *
+ *	Amul Kumar Saha <amul.saha@samsung.com>
+ *	OTP support
+ *	Copyright (C) SAMSUNG Electronics, 2009
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
@@ -308,6 +312,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
 EXPORT_SYMBOL(flexonenand_region);

 /**
+ * onenand_otp_command - Send OTP specific command to OneNAND device
+ * @param mtd	 MTD device structure
+ * @param cmd	 the command to be sent
+ * @param addr	 offset to read from or write to
+ * @param len	 number of bytes to read or write
+ */
+static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
+				size_t len)
+{
+	struct onenand_chip *this = mtd->priv;
+	int value, block, page;
+
+	/* Address translation */
+	switch (cmd) {
+	case ONENAND_CMD_OTP_ACCESS:
+		block = (int) (addr >> this->erase_shift);
+		page = -1;
+		break;
+
+	default:
+		block = (int) (addr >> this->erase_shift);
+		page = (int) (addr >> this->page_shift);
+
+		if (ONENAND_IS_2PLANE(this)) {
+			/* Make the even block number */
+			block &= ~1;
+			/* Is it the odd plane? */
+			if (addr & this->writesize)
+				block++;
+			page >>= 1;
+		}
+		page &= this->page_mask;
+		break;
+	}
+
+	if (block != -1) {
+		/* Write 'DFS, FBA' of Flash */
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+	}
+
+	if (page != -1) {
+		/* Now we use page size operation */
+		int sectors = 4, count = 4;
+		int dataram;
+
+		switch (cmd) {
+		default:
+			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+				cmd = ONENAND_CMD_2X_PROG;
+			dataram = ONENAND_CURRENT_BUFFERRAM(this);
+			break;
+		}
+
+		/* Write 'FPA, FSA' of Flash */
+		value = onenand_page_address(page, sectors);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS8);
+
+		/* Write 'BSA, BSC' of DataRAM */
+		value = onenand_buffer_address(dataram, sectors, count);
+		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+	}
+
+	/* Interrupt clear */
+	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+	/* Write command */
+	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+	return 0;
+}
+
+/**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd		MTD device structure
  * @param cmd		the command to be sent
@@ -1879,7 +1958,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
 			if (ret) {
 				written -= prevlen;
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %d\n", ret);
 				break;
 			}

@@ -1905,14 +1985,16 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			/* In partial page write we don't update bufferram */
 			onenand_update_bufferram(mtd, to, !ret && !subpage);
 			if (ret) {
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %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);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						verify failed %d\n", ret);
 				break;
 			}

@@ -1945,6 +2027,134 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,


 /**
+ * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * @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
+ *
+ * OneNAND write out-of-band only for OTP
+ */
+static int onenand_otp_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;
+	int block, value, status;
+
+	to += ops->ooboffs;
+
+	/* Initialize retlen, in case of early exit */
+	ops->oobretlen = 0;
+
+	oobsize = mtd->oobsize;
+
+	column = to & (mtd->oobsize - 1);
+
+	oobbuf = this->oob_buf;
+
+	/* Loop until all data write */
+	while (written < len) {
+		int thislen = min_t(int, oobsize, len - written);
+
+		cond_resched();
+
+		block = (int) (to >> this->erase_shift);
+		/*
+		 * Write 'DFS, FBA' of Flash
+		 * Add: F100h DQ=DFS, FBA
+		 */
+
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+
+		/*
+		 * Select DataRAM for DDP
+		 * Add: F101h DQ=DBS
+		 */
+
+		value = onenand_bufferram_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS2);
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+
+		/*
+		 * Enter OTP access mode
+		 */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
+
+		/* We send data to spare ram with oobsize
+		 * to prevent byte access */
+		memcpy(oobbuf + column, buf, thislen);
+
+		/*
+		 * Write Data into DataRAM
+		 * Add: 8th Word
+		 * in sector0/spare/page0
+		 * DQ=XXFCh
+		 */
+		this->write_bufferram(mtd, ONENAND_SPARERAM,
+					oobbuf, 0, mtd->oobsize);
+
+		onenand_otp_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;
+		}
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
+
+		status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+		status &= 0x60;
+
+		if (status == 0x60) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else if (status == 0x20) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
+		} else if (status == 0x40) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else {
+			printk(KERN_DEBUG "Reboot to check\n");
+		}
+
+		written += thislen;
+		if (written == len)
+			break;
+
+		to += mtd->writesize;
+		buf += thislen;
+		column = 0;
+	}
+
+	ops->oobretlen = written;
+
+	return ret;
+}
+
+/**
  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
  * @param mtd		MTD device structure
  * @param to		offset to write to
@@ -2659,11 +2869,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 	struct mtd_oob_ops ops;
 	int ret;

-	/* Enter OTP access mode */
-	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
-	this->wait(mtd, FL_OTPING);
-
 	if (FLEXONENAND(this)) {
+
+		/* Enter OTP access mode */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
 		/*
 		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
 		 * main area of page 49.
@@ -2674,19 +2884,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 		ops.oobbuf = NULL;
 		ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
 		*retlen = ops.retlen;
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
 	} else {
 		ops.mode = MTD_OOB_PLACE;
 		ops.ooblen = len;
 		ops.oobbuf = buf;
 		ops.ooboffs = 0;
-		ret = onenand_write_oob_nolock(mtd, from, &ops);
+		ret = onenand_otp_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;
 }

@@ -2717,16 +2927,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t 
len,
 	if (density < ONENAND_DEVICE_DENSITY_512Mb)
 		otp_pages = 20;
 	else
-		otp_pages = 10;
+		otp_pages = 50;

 	if (mode == MTD_OTP_FACTORY) {
 		from += mtd->writesize * otp_pages;
-		otp_pages = 64 - otp_pages;
+		otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
 	}

 	/* Check User/Factory boundary */
-	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
-		return 0;
+	if (mode == MTD_OTP_USER) {
+		if (((mtd->writesize * otp_pages) - (from + len)) < 0)
+			return 0;
+	} else {
+		if (((mtd->writesize * otp_pages) - len) < 0)
+			return 0;
+	}

 	onenand_get_device(mtd, FL_OTPING);
 	while (len > 0 && otp_pages > 0) {
@@ -2749,13 +2964,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t 
len,
 			*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;
+			buf += tmp_retlen;
+			len -= tmp_retlen;
+			*retlen += tmp_retlen;

 			if (ret)
 				break;
@@ -2872,17 +3086,6 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
 						 : mtd->oobsize);
 	/*
-	 * Note: OTP lock operation
-	 *       OTP block : 0xXXFC
-	 *       1st block : 0xXXF3 (If chip support)
-	 *       Both      : 0xXXF0 (If chip support)
-	 */
-	if (FLEXONENAND(this))
-		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
-	else
-		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.
 	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
@@ -2892,6 +3095,32 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	from = 0;
 	len = FLEXONENAND(this) ? mtd->writesize : 16;

+	/*
+	 * Note: OTP lock operation
+	 *       OTP block : 0xXXFC			XX 1111 1100
+	 *       1st block : 0xXXF3 (If chip support)	XX 1111 0011
+	 *       Both      : 0xXXF0 (If chip support)	XX 1111 0000
+	 */
+	if (FLEXONENAND(this)) {
+
+#ifdef CONFIG_ONENAND_OTP_AREA
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
+#elif CONFIG_ONENAND_OTP_BLOCK0
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF3;
+#elif CONFIG_ONENAND_OTP_AREA_BLOCK0
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF0;
+#endif	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	} else {
+
+#ifdef CONFIG_ONENAND_OTP_AREA
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
+#elif CONFIG_ONENAND_OTP_BLOCK0
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xF3;
+#elif CONFIG_ONENAND_OTP_AREA_BLOCK0
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xF0;
+#endif	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	}
+
 	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);

 	return ret ? : retlen;
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 8ed8733..7bd1512 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -152,6 +152,8 @@ struct onenand_chip {
 /*
  * Helper macros
  */
+#define ONENAND_PAGES_PER_BLOCK        (1<<6)
+
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-08-17 10:43 [PATCH] [OneNAND] OTP support re-implementation 1/1 Amul Kumar Saha
@ 2009-08-26  5:41 ` Amul Kumar Saha
  2009-08-26  5:58   ` Artem Bityutskiy
  0 siblings, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-08-26  5:41 UTC (permalink / raw)
  To: linux-mtd; +Cc: dedekind, kyungmin.park, David Woodhouse

Hi,

Resending the patch, as there was a line-wrap issue in the earlier post.

With Regards,
Amul Kumar Saha


Re-implemented OTP support for OneNAND
Added following features to OneNAND
	1. Lock only 1st Block in OneNAND
	2. Lock BOTH 1st Block and OTP Block in OneNAND

Signed-off-by: Amul Kumar Saha <amul.saha@samsung.com>
---
 drivers/mtd/onenand/Kconfig        |   19 ++
 drivers/mtd/onenand/onenand_base.c |  291 +++++++++++++++++++++++++++++++++----
 include/linux/mtd/onenand.h        |    2
 3 files changed, 281 insertions(+), 31 deletions(-)

diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 79fa79e..07ca80c 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -48,6 +48,25 @@ config MTD_ONENAND_OTP

 	  OTP block is fully-guaranteed to be a valid block.

+if MTD_ONENAND_OTP
+
+config ONENAND_OTP_AREA
+	bool "OTP area ONLY"
+	depends on MTD_ONENAND_OTP
+	select ON_OTP_AREA
+
+config ONENAND_OTP_BLOCK0
+	bool "Block[0] ONLY"
+	depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA
+	select ON_OTP_BLOCK0
+
+config ONENAND_OTP_AREA_BLOCK0
+	bool "BOTH OTP area AND Block[0]"
+	depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA && !ONENAND_OTP_BLOCK0
+	select ON_OTP_AREA_BLOCK0
+
+endif #MTD_ONENAND_OTP
+
 config MTD_ONENAND_2X_PROGRAM
 	bool "OneNAND 2X program support"
 	help
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 6e82909..3da9d63 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -13,6 +13,10 @@
  *	Flex-OneNAND support
  *	Copyright (C) Samsung Electronics, 2008
  *
+ *	Amul Kumar Saha <amul.saha@samsung.com>
+ *	OTP support
+ *	Copyright (C) SAMSUNG Electronics, 2009
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
@@ -308,6 +312,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
 EXPORT_SYMBOL(flexonenand_region);

 /**
+ * onenand_otp_command - Send OTP specific command to OneNAND device
+ * @param mtd	 MTD device structure
+ * @param cmd	 the command to be sent
+ * @param addr	 offset to read from or write to
+ * @param len	 number of bytes to read or write
+ */
+static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
+				size_t len)
+{
+	struct onenand_chip *this = mtd->priv;
+	int value, block, page;
+
+	/* Address translation */
+	switch (cmd) {
+	case ONENAND_CMD_OTP_ACCESS:
+		block = (int) (addr >> this->erase_shift);
+		page = -1;
+		break;
+
+	default:
+		block = (int) (addr >> this->erase_shift);
+		page = (int) (addr >> this->page_shift);
+
+		if (ONENAND_IS_2PLANE(this)) {
+			/* Make the even block number */
+			block &= ~1;
+			/* Is it the odd plane? */
+			if (addr & this->writesize)
+				block++;
+			page >>= 1;
+		}
+		page &= this->page_mask;
+		break;
+	}
+
+	if (block != -1) {
+		/* Write 'DFS, FBA' of Flash */
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+	}
+
+	if (page != -1) {
+		/* Now we use page size operation */
+		int sectors = 4, count = 4;
+		int dataram;
+
+		switch (cmd) {
+		default:
+			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+				cmd = ONENAND_CMD_2X_PROG;
+			dataram = ONENAND_CURRENT_BUFFERRAM(this);
+			break;
+		}
+
+		/* Write 'FPA, FSA' of Flash */
+		value = onenand_page_address(page, sectors);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS8);
+
+		/* Write 'BSA, BSC' of DataRAM */
+		value = onenand_buffer_address(dataram, sectors, count);
+		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+	}
+
+	/* Interrupt clear */
+	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+	/* Write command */
+	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+	return 0;
+}
+
+/**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd		MTD device structure
  * @param cmd		the command to be sent
@@ -1879,7 +1958,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
 			if (ret) {
 				written -= prevlen;
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %d\n", ret);
 				break;
 			}

@@ -1905,14 +1985,16 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			/* In partial page write we don't update bufferram */
 			onenand_update_bufferram(mtd, to, !ret && !subpage);
 			if (ret) {
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %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);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						verify failed %d\n", ret);
 				break;
 			}

@@ -1945,6 +2027,134 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,


 /**
+ * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * @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
+ *
+ * OneNAND write out-of-band only for OTP
+ */
+static int onenand_otp_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;
+	int block, value, status;
+
+	to += ops->ooboffs;
+
+	/* Initialize retlen, in case of early exit */
+	ops->oobretlen = 0;
+
+	oobsize = mtd->oobsize;
+
+	column = to & (mtd->oobsize - 1);
+
+	oobbuf = this->oob_buf;
+
+	/* Loop until all data write */
+	while (written < len) {
+		int thislen = min_t(int, oobsize, len - written);
+
+		cond_resched();
+
+		block = (int) (to >> this->erase_shift);
+		/*
+		 * Write 'DFS, FBA' of Flash
+		 * Add: F100h DQ=DFS, FBA
+		 */
+
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+
+		/*
+		 * Select DataRAM for DDP
+		 * Add: F101h DQ=DBS
+		 */
+
+		value = onenand_bufferram_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS2);
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+
+		/*
+		 * Enter OTP access mode
+		 */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
+
+		/* We send data to spare ram with oobsize
+		 * to prevent byte access */
+		memcpy(oobbuf + column, buf, thislen);
+
+		/*
+		 * Write Data into DataRAM
+		 * Add: 8th Word
+		 * in sector0/spare/page0
+		 * DQ=XXFCh
+		 */
+		this->write_bufferram(mtd, ONENAND_SPARERAM,
+					oobbuf, 0, mtd->oobsize);
+
+		onenand_otp_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;
+		}
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
+
+		status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+		status &= 0x60;
+
+		if (status == 0x60) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else if (status == 0x20) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
+		} else if (status == 0x40) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else {
+			printk(KERN_DEBUG "Reboot to check\n");
+		}
+
+		written += thislen;
+		if (written == len)
+			break;
+
+		to += mtd->writesize;
+		buf += thislen;
+		column = 0;
+	}
+
+	ops->oobretlen = written;
+
+	return ret;
+}
+
+/**
  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
  * @param mtd		MTD device structure
  * @param to		offset to write to
@@ -2659,11 +2869,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 	struct mtd_oob_ops ops;
 	int ret;

-	/* Enter OTP access mode */
-	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
-	this->wait(mtd, FL_OTPING);
-
 	if (FLEXONENAND(this)) {
+
+		/* Enter OTP access mode */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
 		/*
 		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
 		 * main area of page 49.
@@ -2674,19 +2884,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 		ops.oobbuf = NULL;
 		ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
 		*retlen = ops.retlen;
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
 	} else {
 		ops.mode = MTD_OOB_PLACE;
 		ops.ooblen = len;
 		ops.oobbuf = buf;
 		ops.ooboffs = 0;
-		ret = onenand_write_oob_nolock(mtd, from, &ops);
+		ret = onenand_otp_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;
 }

@@ -2717,16 +2927,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 	if (density < ONENAND_DEVICE_DENSITY_512Mb)
 		otp_pages = 20;
 	else
-		otp_pages = 10;
+		otp_pages = 50;

 	if (mode == MTD_OTP_FACTORY) {
 		from += mtd->writesize * otp_pages;
-		otp_pages = 64 - otp_pages;
+		otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
 	}

 	/* Check User/Factory boundary */
-	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
-		return 0;
+	if (mode == MTD_OTP_USER) {
+		if (((mtd->writesize * otp_pages) - (from + len)) < 0)
+			return 0;
+	} else {
+		if (((mtd->writesize * otp_pages) - len) < 0)
+			return 0;
+	}

 	onenand_get_device(mtd, FL_OTPING);
 	while (len > 0 && otp_pages > 0) {
@@ -2749,13 +2964,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 			*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;
+			buf += tmp_retlen;
+			len -= tmp_retlen;
+			*retlen += tmp_retlen;

 			if (ret)
 				break;
@@ -2872,17 +3086,6 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
 						 : mtd->oobsize);
 	/*
-	 * Note: OTP lock operation
-	 *       OTP block : 0xXXFC
-	 *       1st block : 0xXXF3 (If chip support)
-	 *       Both      : 0xXXF0 (If chip support)
-	 */
-	if (FLEXONENAND(this))
-		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
-	else
-		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.
 	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
@@ -2892,6 +3095,32 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	from = 0;
 	len = FLEXONENAND(this) ? mtd->writesize : 16;

+	/*
+	 * Note: OTP lock operation
+	 *       OTP block : 0xXXFC			XX 1111 1100
+	 *       1st block : 0xXXF3 (If chip support)	XX 1111 0011
+	 *       Both      : 0xXXF0 (If chip support)	XX 1111 0000
+	 */
+	if (FLEXONENAND(this)) {
+
+#ifdef CONFIG_ONENAND_OTP_AREA
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
+#elif CONFIG_ONENAND_OTP_BLOCK0
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF3;
+#elif CONFIG_ONENAND_OTP_AREA_BLOCK0
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF0;
+#endif	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	} else {
+
+#ifdef CONFIG_ONENAND_OTP_AREA
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
+#elif CONFIG_ONENAND_OTP_BLOCK0
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xF3;
+#elif CONFIG_ONENAND_OTP_AREA_BLOCK0
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xF0;
+#endif	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	}
+
 	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);

 	return ret ? : retlen;
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 8ed8733..7bd1512 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -152,6 +152,8 @@ struct onenand_chip {
 /*
  * Helper macros
  */
+#define ONENAND_PAGES_PER_BLOCK        (1<<6)
+
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-08-26  5:41 ` Amul Kumar Saha
@ 2009-08-26  5:58   ` Artem Bityutskiy
  2009-08-27  6:37     ` Amul Kumar Saha
  0 siblings, 1 reply; 27+ messages in thread
From: Artem Bityutskiy @ 2009-08-26  5:58 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: dedekind, David Woodhouse, kyungmin.park, linux-mtd

On 08/26/2009 08:41 AM, Amul Kumar Saha wrote:
> Resending the patch, as there was a line-wrap issue in the earlier post.
>
> With Regards,
> Amul Kumar Saha
>
>
> Re-implemented OTP support for OneNAND
> Added following features to OneNAND
> 	1. Lock only 1st Block in OneNAND
> 	2. Lock BOTH 1st Block and OTP Block in OneNAND

-ENOPARSE

Large patches like this normally require good explanations of what is
done, why it is done, which problem it solves, how it is done, etc.

-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-08-26  5:58   ` Artem Bityutskiy
@ 2009-08-27  6:37     ` Amul Kumar Saha
  2009-08-28 13:38       ` Artem Bityutskiy
  0 siblings, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-08-27  6:37 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: dedekind, David Woodhouse, kyungmin.park, linux-mtd

>> Re-implemented OTP support for OneNAND
>> Added following features to OneNAND
>> 1. Lock only 1st Block in OneNAND
>> 2. Lock BOTH 1st Block and OTP Block in OneNAND
>
> -ENOPARSE
>
> Large patches like this normally require good explanations of what is
> done, why it is done, which problem it solves, how it is done, etc.
>

What is OTP in OneNAND?
The device includes,
1. one block-sized OTP (One Time Programmable) area and
2. user-controlled 1st block OTP(Block 0)
that can be used to increase system security or to provide identification capabilities.

What is done?
In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0) 
can be used as OTP area.
This area, available to the user, can be configured and locked with secured user information.
The OTP block can be read, programmed and locked using the same operations as any other NAND Flash 
Array
memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed 
to be a valid block.

Why it is done? (Impact)
Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental 
re-programming of data stored in the 1st block and OTP Block.

Which problem it solves?
OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not 
working with OneNAND devices.
Have observed the following in current OTP OneNAND Implmentation,
1. DataSheet specific sequence to lock the OTP Area is not followed.
2. Certain functions are quiet generic to cope with OTP specific activity.
This patch re-implements OTP support for OneNAND device.

How it is done?
For all blocks, 8th word is available to the user.
However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
Therefore, in case of OTP Block, user usage on this area is prohibited.
Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the 
process of issuing an OTP-Lock.
The possible conditions are:-
1. Only 1st Block Lock
2. Only OTP Block Lock
3. Lock both the 1st Block and the OTP Block

What feature additions have been done in this patch?
This patch adds feature for:-
1. Only 1st Block Lock
2. Lock both the 1st Block and the OTP Block 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-08-27  6:37     ` Amul Kumar Saha
@ 2009-08-28 13:38       ` Artem Bityutskiy
  2009-08-31  9:19         ` Amul Kumar Saha
  0 siblings, 1 reply; 27+ messages in thread
From: Artem Bityutskiy @ 2009-08-28 13:38 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: David Woodhouse, kyungmin.park, linux-mtd

On Thu, 2009-08-27 at 12:07 +0530, Amul Kumar Saha wrote:
> >> Re-implemented OTP support for OneNAND
> >> Added following features to OneNAND
> >> 1. Lock only 1st Block in OneNAND
> >> 2. Lock BOTH 1st Block and OTP Block in OneNAND
> >
> > -ENOPARSE
> >
> > Large patches like this normally require good explanations of what is
> > done, why it is done, which problem it solves, how it is done, etc.
> >
> 
> What is OTP in OneNAND?
> The device includes,
> 1. one block-sized OTP (One Time Programmable) area and
> 2. user-controlled 1st block OTP(Block 0)
> that can be used to increase system security or to provide identification capabilities.
> 
> What is done?
> In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0) 
> can be used as OTP area.
> This area, available to the user, can be configured and locked with secured user information.
> The OTP block can be read, programmed and locked using the same operations as any other NAND Flash 
> Array
> memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed 
> to be a valid block.
> 
> Why it is done? (Impact)
> Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental 
> re-programming of data stored in the 1st block and OTP Block.
> 
> Which problem it solves?
> OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not 
> working with OneNAND devices.
> Have observed the following in current OTP OneNAND Implmentation,
> 1. DataSheet specific sequence to lock the OTP Area is not followed.
> 2. Certain functions are quiet generic to cope with OTP specific activity.
> This patch re-implements OTP support for OneNAND device.
> 
> How it is done?
> For all blocks, 8th word is available to the user.
> However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
> Therefore, in case of OTP Block, user usage on this area is prohibited.
> Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the 
> process of issuing an OTP-Lock.
> The possible conditions are:-
> 1. Only 1st Block Lock
> 2. Only OTP Block Lock
> 3. Lock both the 1st Block and the OTP Block
> 
> What feature additions have been done in this patch?
> This patch adds feature for:-
> 1. Only 1st Block Lock
> 2. Lock both the 1st Block and the OTP Block 

Could you insert the description to the patch and re-send it, please?

-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-08-28 13:38       ` Artem Bityutskiy
@ 2009-08-31  9:19         ` Amul Kumar Saha
  2009-08-31  9:34           ` Kyungmin Park
  0 siblings, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-08-31  9:19 UTC (permalink / raw)
  To: dedekind1; +Cc: linux-mtd, kyungmin.park, David Woodhouse

What is OTP in OneNAND?
The device includes,
1. one block-sized OTP (One Time Programmable) area and
2. user-controlled 1st block OTP(Block 0)
that can be used to increase system security or to provide identification capabilities.

What is done?
In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0)
can be used as OTP area.
This area, available to the user, can be configured and locked with secured user information.
The OTP block can be read, programmed and locked using the same operations as any other NAND Flash
Array
memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed
to be a valid block.

Why it is done? (Impact)
Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental
re-programming of data stored in the 1st block and OTP Block.

Which problem it solves?
OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not
working with OneNAND devices.
Have observed the following in current OTP OneNAND Implmentation,
1. DataSheet specific sequence to lock the OTP Area is not followed.
2. Certain functions are quiet generic to cope with OTP specific activity.
This patch re-implements OTP support for OneNAND device.

How it is done?
For all blocks, 8th word is available to the user.
However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
Therefore, in case of OTP Block, user usage on this area is prohibited.
Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the
process of issuing an OTP-Lock.
The possible conditions are:-
1. Only 1st Block Lock
2. Only OTP Block Lock
3. Lock both the 1st Block and the OTP Block

What Other feature additions have been done in this patch?
This patch adds feature for:-
1. Only 1st Block Lock
2. Lock both the 1st Block and the OTP Blocks

Re-implemented OTP support for OneNAND
Added following features to OneNAND
	1. Lock only 1st Block in OneNAND
	2. Lock BOTH 1st Block and OTP Block in OneNAND

Signed-off-by: Amul Kumar Saha <amul.saha@samsung.com>
---
 drivers/mtd/onenand/Kconfig        |   19 ++
 drivers/mtd/onenand/onenand_base.c |  291 +++++++++++++++++++++++++++++++++----
 include/linux/mtd/onenand.h        |    2
 3 files changed, 281 insertions(+), 31 deletions(-)

diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 79fa79e..07ca80c 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -48,6 +48,25 @@ config MTD_ONENAND_OTP

 	  OTP block is fully-guaranteed to be a valid block.

+if MTD_ONENAND_OTP
+
+config ONENAND_OTP_AREA
+	bool "OTP area ONLY"
+	depends on MTD_ONENAND_OTP
+	select ON_OTP_AREA
+
+config ONENAND_OTP_BLOCK0
+	bool "Block[0] ONLY"
+	depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA
+	select ON_OTP_BLOCK0
+
+config ONENAND_OTP_AREA_BLOCK0
+	bool "BOTH OTP area AND Block[0]"
+	depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA && !ONENAND_OTP_BLOCK0
+	select ON_OTP_AREA_BLOCK0
+
+endif #MTD_ONENAND_OTP
+
 config MTD_ONENAND_2X_PROGRAM
 	bool "OneNAND 2X program support"
 	help
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 6e82909..7c4fb75 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -13,6 +13,10 @@
  *	Flex-OneNAND support
  *	Copyright (C) Samsung Electronics, 2008
  *
+ *	Amul Kumar Saha <amul.saha@samsung.com>
+ *	OTP support
+ *	Copyright (C) SAMSUNG Electronics, 2009
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
@@ -308,6 +312,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
 EXPORT_SYMBOL(flexonenand_region);

 /**
+ * onenand_otp_command - Send OTP specific command to OneNAND device
+ * @param mtd	 MTD device structure
+ * @param cmd	 the command to be sent
+ * @param addr	 offset to read from or write to
+ * @param len	 number of bytes to read or write
+ */
+static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
+				size_t len)
+{
+	struct onenand_chip *this = mtd->priv;
+	int value, block, page;
+
+	/* Address translation */
+	switch (cmd) {
+	case ONENAND_CMD_OTP_ACCESS:
+		block = (int) (addr >> this->erase_shift);
+		page = -1;
+		break;
+
+	default:
+		block = (int) (addr >> this->erase_shift);
+		page = (int) (addr >> this->page_shift);
+
+		if (ONENAND_IS_2PLANE(this)) {
+			/* Make the even block number */
+			block &= ~1;
+			/* Is it the odd plane? */
+			if (addr & this->writesize)
+				block++;
+			page >>= 1;
+		}
+		page &= this->page_mask;
+		break;
+	}
+
+	if (block != -1) {
+		/* Write 'DFS, FBA' of Flash */
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+	}
+
+	if (page != -1) {
+		/* Now we use page size operation */
+		int sectors = 4, count = 4;
+		int dataram;
+
+		switch (cmd) {
+		default:
+			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+				cmd = ONENAND_CMD_2X_PROG;
+			dataram = ONENAND_CURRENT_BUFFERRAM(this);
+			break;
+		}
+
+		/* Write 'FPA, FSA' of Flash */
+		value = onenand_page_address(page, sectors);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS8);
+
+		/* Write 'BSA, BSC' of DataRAM */
+		value = onenand_buffer_address(dataram, sectors, count);
+		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+	}
+
+	/* Interrupt clear */
+	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+	/* Write command */
+	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+	return 0;
+}
+
+/**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd		MTD device structure
  * @param cmd		the command to be sent
@@ -1879,7 +1958,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
 			if (ret) {
 				written -= prevlen;
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %d\n", ret);
 				break;
 			}

@@ -1905,14 +1985,16 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			/* In partial page write we don't update bufferram */
 			onenand_update_bufferram(mtd, to, !ret && !subpage);
 			if (ret) {
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %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);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						verify failed %d\n", ret);
 				break;
 			}

@@ -1945,6 +2027,134 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,


 /**
+ * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * @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
+ *
+ * OneNAND write out-of-band only for OTP
+ */
+static int onenand_otp_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;
+	int block, value, status;
+
+	to += ops->ooboffs;
+
+	/* Initialize retlen, in case of early exit */
+	ops->oobretlen = 0;
+
+	oobsize = mtd->oobsize;
+
+	column = to & (mtd->oobsize - 1);
+
+	oobbuf = this->oob_buf;
+
+	/* Loop until all data write */
+	while (written < len) {
+		int thislen = min_t(int, oobsize, len - written);
+
+		cond_resched();
+
+		block = (int) (to >> this->erase_shift);
+		/*
+		 * Write 'DFS, FBA' of Flash
+		 * Add: F100h DQ=DFS, FBA
+		 */
+
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+
+		/*
+		 * Select DataRAM for DDP
+		 * Add: F101h DQ=DBS
+		 */
+
+		value = onenand_bufferram_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS2);
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+
+		/*
+		 * Enter OTP access mode
+		 */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
+
+		/* We send data to spare ram with oobsize
+		 * to prevent byte access */
+		memcpy(oobbuf + column, buf, thislen);
+
+		/*
+		 * Write Data into DataRAM
+		 * Add: 8th Word
+		 * in sector0/spare/page0
+		 * DQ=XXFCh
+		 */
+		this->write_bufferram(mtd, ONENAND_SPARERAM,
+					oobbuf, 0, mtd->oobsize);
+
+		onenand_otp_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;
+		}
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
+
+		status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+		status &= 0x60;
+
+		if (status == 0x60) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else if (status == 0x20) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
+		} else if (status == 0x40) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else {
+			printk(KERN_DEBUG "Reboot to check\n");
+		}
+
+		written += thislen;
+		if (written == len)
+			break;
+
+		to += mtd->writesize;
+		buf += thislen;
+		column = 0;
+	}
+
+	ops->oobretlen = written;
+
+	return ret;
+}
+
+/**
  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
  * @param mtd		MTD device structure
  * @param to		offset to write to
@@ -2659,11 +2869,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 	struct mtd_oob_ops ops;
 	int ret;

-	/* Enter OTP access mode */
-	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
-	this->wait(mtd, FL_OTPING);
-
 	if (FLEXONENAND(this)) {
+
+		/* Enter OTP access mode */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
 		/*
 		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
 		 * main area of page 49.
@@ -2674,19 +2884,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 		ops.oobbuf = NULL;
 		ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
 		*retlen = ops.retlen;
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
 	} else {
 		ops.mode = MTD_OOB_PLACE;
 		ops.ooblen = len;
 		ops.oobbuf = buf;
 		ops.ooboffs = 0;
-		ret = onenand_write_oob_nolock(mtd, from, &ops);
+		ret = onenand_otp_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;
 }

@@ -2717,16 +2927,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 	if (density < ONENAND_DEVICE_DENSITY_512Mb)
 		otp_pages = 20;
 	else
-		otp_pages = 10;
+		otp_pages = 50;

 	if (mode == MTD_OTP_FACTORY) {
 		from += mtd->writesize * otp_pages;
-		otp_pages = 64 - otp_pages;
+		otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
 	}

 	/* Check User/Factory boundary */
-	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
-		return 0;
+	if (mode == MTD_OTP_USER) {
+		if (((mtd->writesize * otp_pages) - (from + len)) < 0)
+			return 0;
+	} else {
+		if (((mtd->writesize * otp_pages) - len) < 0)
+			return 0;
+	}

 	onenand_get_device(mtd, FL_OTPING);
 	while (len > 0 && otp_pages > 0) {
@@ -2749,13 +2964,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 			*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;
+			buf += tmp_retlen;
+			len -= tmp_retlen;
+			*retlen += tmp_retlen;

 			if (ret)
 				break;
@@ -2872,17 +3086,6 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
 						 : mtd->oobsize);
 	/*
-	 * Note: OTP lock operation
-	 *       OTP block : 0xXXFC
-	 *       1st block : 0xXXF3 (If chip support)
-	 *       Both      : 0xXXF0 (If chip support)
-	 */
-	if (FLEXONENAND(this))
-		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
-	else
-		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.
 	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
@@ -2892,6 +3095,32 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	from = 0;
 	len = FLEXONENAND(this) ? mtd->writesize : 16;

+	/*
+	 * Note: OTP lock operation
+	 *       OTP block : 0xXXFC			XX 1111 1100
+	 *       1st block : 0xXXF3 (If chip support)	XX 1111 0011
+	 *       Both      : 0xXXF0 (If chip support)	XX 1111 0000
+	 */
+	if (FLEXONENAND(this)) {
+
+#ifdef CONFIG_ONENAND_OTP_AREA
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
+#elif defined(CONFIG_ONENAND_OTP_BLOCK0)
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF3;
+#elif defined(CONFIG_ONENAND_OTP_AREA_BLOCK0)
+		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF0;
+#endif	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	} else {
+
+#ifdef CONFIG_ONENAND_OTP_AREA
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
+#elif defined(CONFIG_ONENAND_OTP_BLOCK0)
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xF3;
+#elif defined(CONFIG_ONENAND_OTP_AREA_BLOCK0)
+		buf[ONENAND_OTP_LOCK_OFFSET] = 0xF0;
+#endif	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	}
+
 	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);

 	return ret ? : retlen;
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 8ed8733..7bd1512 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -152,6 +152,8 @@ struct onenand_chip {
 /*
  * Helper macros
  */
+#define ONENAND_PAGES_PER_BLOCK        (1<<6)
+
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-08-31  9:19         ` Amul Kumar Saha
@ 2009-08-31  9:34           ` Kyungmin Park
  2009-09-02  5:55             ` Amul Kumar Saha
  2009-09-02  5:59             ` Amul Kumar Saha
  0 siblings, 2 replies; 27+ messages in thread
From: Kyungmin Park @ 2009-08-31  9:34 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: David Woodhouse, linux-mtd, dedekind1

Hi,

@@ -2717,16 +2927,21 @@ static int onenand_otp_walk(struct mtd_info
*mtd, loff_t from, size_t len,
       if (density < ONENAND_DEVICE_DENSITY_512Mb)
               otp_pages = 20;
       else
-               otp_pages = 10;
+               otp_pages = 50;

Is it always 50 pages? Is it right? why I think it's 10. please check
the previous OneNAND spec.
At least the latest OneNAND spec say it's 50. flex or not.

> +       /*
> +        * Note: OTP lock operation
> +        *       OTP block : 0xXXFC                     XX 1111 1100
> +        *       1st block : 0xXXF3 (If chip support)   XX 1111 0011
> +        *       Both      : 0xXXF0 (If chip support)   XX 1111 0000
> +        */
> +       if (FLEXONENAND(this)) {
> +
> +#ifdef CONFIG_ONENAND_OTP_AREA
> +               buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
> +#elif defined(CONFIG_ONENAND_OTP_BLOCK0)
> +               buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF3;
> +#elif defined(CONFIG_ONENAND_OTP_AREA_BLOCK0)
> +               buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF0;
> +#endif /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
> +       } else {
> +
> +#ifdef CONFIG_ONENAND_OTP_AREA
> +               buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
> +#elif defined(CONFIG_ONENAND_OTP_BLOCK0)
> +               buf[ONENAND_OTP_LOCK_OFFSET] = 0xF3;
> +#elif defined(CONFIG_ONENAND_OTP_AREA_BLOCK0)
> +               buf[ONENAND_OTP_LOCK_OFFSET] = 0xF0;
> +#endif /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
> +       }
> +

it's too much #ifdef, #endif. Introuduce the opt_lock_offset and set
the proper offset based on flex or not.

And the set the buf[otp_lock_offset] = proper value.

Thank you,
Kyungmin Park

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-08-31  9:34           ` Kyungmin Park
@ 2009-09-02  5:55             ` Amul Kumar Saha
  2009-09-02  5:59             ` Amul Kumar Saha
  1 sibling, 0 replies; 27+ messages in thread
From: Amul Kumar Saha @ 2009-09-02  5:55 UTC (permalink / raw)
  To: Kyungmin Park; +Cc: linux-mtd, David Woodhouse, dedekind1

>
> @@ -2717,16 +2927,21 @@ static int onenand_otp_walk(struct mtd_info
> *mtd, loff_t from, size_t len,
>       if (density < ONENAND_DEVICE_DENSITY_512Mb)
>               otp_pages = 20;
>       else
> -               otp_pages = 10;
> +               otp_pages = 50;
>
> Is it always 50 pages? Is it right? why I think it's 10. please check
> the previous OneNAND spec.
> At least the latest OneNAND spec say it's 50. flex or not.
>

I've checked different OneNAND specs and 50 pages have been allocated for the User Area in all of 
those.

>> +       if (FLEXONENAND(this)) {
>> +
>> +#ifdef CONFIG_ONENAND_OTP_AREA
>> +               buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
>> +#elif defined(CONFIG_ONENAND_OTP_BLOCK0)
>> +               buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF3;
>> +#elif defined(CONFIG_ONENAND_OTP_AREA_BLOCK0)
>> +               buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF0;
>> +#endif /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
>> +       } else {
>> +
>> +#ifdef CONFIG_ONENAND_OTP_AREA
>> +               buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
>> +#elif defined(CONFIG_ONENAND_OTP_BLOCK0)
>> +               buf[ONENAND_OTP_LOCK_OFFSET] = 0xF3;
>> +#elif defined(CONFIG_ONENAND_OTP_AREA_BLOCK0)
>> +               buf[ONENAND_OTP_LOCK_OFFSET] = 0xF0;
>> +#endif /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
>> +       }
>> +
>
> it's too much #ifdef, #endif. Introuduce the opt_lock_offset and set
> the proper offset based on flex or not.
>
> And the set the buf[otp_lock_offset] = proper value.
>
Accepted and corrected.

Please find the patch in the following mail.

Regards,
Amul

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-08-31  9:34           ` Kyungmin Park
  2009-09-02  5:55             ` Amul Kumar Saha
@ 2009-09-02  5:59             ` Amul Kumar Saha
  2009-09-02  6:10               ` Artem Bityutskiy
  1 sibling, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-09-02  5:59 UTC (permalink / raw)
  To: Kyungmin Park; +Cc: linux-mtd, David Woodhouse, dedekind1

What is OTP in OneNAND?
The device includes,
1. one block-sized OTP (One Time Programmable) area and
2. user-controlled 1st block OTP(Block 0)
that can be used to increase system security or to provide identification capabilities.

What is done?
In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0)
can be used as OTP area.
This area, available to the user, can be configured and locked with secured user information.
The OTP block can be read, programmed and locked using the same operations as any other NAND Flash
Array
memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed
to be a valid block.

Why it is done? (Impact)
Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental
re-programming of data stored in the 1st block and OTP Block.

Which problem it solves?
OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not
working with OneNAND devices.
Have observed the following in current OTP OneNAND Implmentation,
1. DataSheet specific sequence to lock the OTP Area is not followed.
2. Certain functions are quiet generic to cope with OTP specific activity.
This patch re-implements OTP support for OneNAND device.

How it is done?
For all blocks, 8th word is available to the user.
However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
Therefore, in case of OTP Block, user usage on this area is prohibited.
Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the
process of issuing an OTP-Lock.
The possible conditions are:-
1. Only 1st Block Lock
2. Only OTP Block Lock
3. Lock both the 1st Block and the OTP Block

What Other feature additions have been done in this patch?
This patch adds feature for:-
1. Only 1st Block Lock
2. Lock both the 1st Block and the OTP Blocks

Re-implemented OTP support for OneNAND
Added following features to OneNAND
	1. Lock only 1st Block in OneNAND
	2. Lock BOTH 1st Block and OTP Block in OneNAND

Signed-off-by: Amul Kumar Saha <amul.saha@samsung.com>
---
 drivers/mtd/onenand/Kconfig        |   19 ++
 drivers/mtd/onenand/onenand_base.c |  283 ++++++++++++++++++++++++++++++++-----
 include/linux/mtd/onenand.h        |    2
 3 files changed, 273 insertions(+), 31 deletions(-)

diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 79fa79e..07ca80c 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -48,6 +48,25 @@ config MTD_ONENAND_OTP

 	  OTP block is fully-guaranteed to be a valid block.

+if MTD_ONENAND_OTP
+
+config ONENAND_OTP_AREA
+	bool "OTP area ONLY"
+	depends on MTD_ONENAND_OTP
+	select ON_OTP_AREA
+
+config ONENAND_OTP_BLOCK0
+	bool "Block[0] ONLY"
+	depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA
+	select ON_OTP_BLOCK0
+
+config ONENAND_OTP_AREA_BLOCK0
+	bool "BOTH OTP area AND Block[0]"
+	depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA && !ONENAND_OTP_BLOCK0
+	select ON_OTP_AREA_BLOCK0
+
+endif #MTD_ONENAND_OTP
+
 config MTD_ONENAND_2X_PROGRAM
 	bool "OneNAND 2X program support"
 	help
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 6e82909..1b11a3e 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -13,6 +13,10 @@
  *	Flex-OneNAND support
  *	Copyright (C) Samsung Electronics, 2008
  *
+ *	Amul Kumar Saha <amul.saha@samsung.com>
+ *	OTP support
+ *	Copyright (C) SAMSUNG Electronics, 2009
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
@@ -308,6 +312,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
 EXPORT_SYMBOL(flexonenand_region);

 /**
+ * onenand_otp_command - Send OTP specific command to OneNAND device
+ * @param mtd	 MTD device structure
+ * @param cmd	 the command to be sent
+ * @param addr	 offset to read from or write to
+ * @param len	 number of bytes to read or write
+ */
+static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
+				size_t len)
+{
+	struct onenand_chip *this = mtd->priv;
+	int value, block, page;
+
+	/* Address translation */
+	switch (cmd) {
+	case ONENAND_CMD_OTP_ACCESS:
+		block = (int) (addr >> this->erase_shift);
+		page = -1;
+		break;
+
+	default:
+		block = (int) (addr >> this->erase_shift);
+		page = (int) (addr >> this->page_shift);
+
+		if (ONENAND_IS_2PLANE(this)) {
+			/* Make the even block number */
+			block &= ~1;
+			/* Is it the odd plane? */
+			if (addr & this->writesize)
+				block++;
+			page >>= 1;
+		}
+		page &= this->page_mask;
+		break;
+	}
+
+	if (block != -1) {
+		/* Write 'DFS, FBA' of Flash */
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+	}
+
+	if (page != -1) {
+		/* Now we use page size operation */
+		int sectors = 4, count = 4;
+		int dataram;
+
+		switch (cmd) {
+		default:
+			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+				cmd = ONENAND_CMD_2X_PROG;
+			dataram = ONENAND_CURRENT_BUFFERRAM(this);
+			break;
+		}
+
+		/* Write 'FPA, FSA' of Flash */
+		value = onenand_page_address(page, sectors);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS8);
+
+		/* Write 'BSA, BSC' of DataRAM */
+		value = onenand_buffer_address(dataram, sectors, count);
+		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+	}
+
+	/* Interrupt clear */
+	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+	/* Write command */
+	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+	return 0;
+}
+
+/**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd		MTD device structure
  * @param cmd		the command to be sent
@@ -1879,7 +1958,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
 			if (ret) {
 				written -= prevlen;
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %d\n", ret);
 				break;
 			}

@@ -1905,14 +1985,16 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			/* In partial page write we don't update bufferram */
 			onenand_update_bufferram(mtd, to, !ret && !subpage);
 			if (ret) {
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %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);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						verify failed %d\n", ret);
 				break;
 			}

@@ -1945,6 +2027,134 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,


 /**
+ * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * @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
+ *
+ * OneNAND write out-of-band only for OTP
+ */
+static int onenand_otp_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;
+	int block, value, status;
+
+	to += ops->ooboffs;
+
+	/* Initialize retlen, in case of early exit */
+	ops->oobretlen = 0;
+
+	oobsize = mtd->oobsize;
+
+	column = to & (mtd->oobsize - 1);
+
+	oobbuf = this->oob_buf;
+
+	/* Loop until all data write */
+	while (written < len) {
+		int thislen = min_t(int, oobsize, len - written);
+
+		cond_resched();
+
+		block = (int) (to >> this->erase_shift);
+		/*
+		 * Write 'DFS, FBA' of Flash
+		 * Add: F100h DQ=DFS, FBA
+		 */
+
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+
+		/*
+		 * Select DataRAM for DDP
+		 * Add: F101h DQ=DBS
+		 */
+
+		value = onenand_bufferram_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS2);
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+
+		/*
+		 * Enter OTP access mode
+		 */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
+
+		/* We send data to spare ram with oobsize
+		 * to prevent byte access */
+		memcpy(oobbuf + column, buf, thislen);
+
+		/*
+		 * Write Data into DataRAM
+		 * Add: 8th Word
+		 * in sector0/spare/page0
+		 * DQ=XXFCh
+		 */
+		this->write_bufferram(mtd, ONENAND_SPARERAM,
+					oobbuf, 0, mtd->oobsize);
+
+		onenand_otp_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;
+		}
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
+
+		status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+		status &= 0x60;
+
+		if (status == 0x60) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else if (status == 0x20) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
+		} else if (status == 0x40) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else {
+			printk(KERN_DEBUG "Reboot to check\n");
+		}
+
+		written += thislen;
+		if (written == len)
+			break;
+
+		to += mtd->writesize;
+		buf += thislen;
+		column = 0;
+	}
+
+	ops->oobretlen = written;
+
+	return ret;
+}
+
+/**
  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
  * @param mtd		MTD device structure
  * @param to		offset to write to
@@ -2659,11 +2869,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 	struct mtd_oob_ops ops;
 	int ret;

-	/* Enter OTP access mode */
-	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
-	this->wait(mtd, FL_OTPING);
-
 	if (FLEXONENAND(this)) {
+
+		/* Enter OTP access mode */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
 		/*
 		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
 		 * main area of page 49.
@@ -2674,19 +2884,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 		ops.oobbuf = NULL;
 		ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
 		*retlen = ops.retlen;
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
 	} else {
 		ops.mode = MTD_OOB_PLACE;
 		ops.ooblen = len;
 		ops.oobbuf = buf;
 		ops.ooboffs = 0;
-		ret = onenand_write_oob_nolock(mtd, from, &ops);
+		ret = onenand_otp_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;
 }

@@ -2717,16 +2927,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 	if (density < ONENAND_DEVICE_DENSITY_512Mb)
 		otp_pages = 20;
 	else
-		otp_pages = 10;
+		otp_pages = 50;

 	if (mode == MTD_OTP_FACTORY) {
 		from += mtd->writesize * otp_pages;
-		otp_pages = 64 - otp_pages;
+		otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
 	}

 	/* Check User/Factory boundary */
-	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
-		return 0;
+	if (mode == MTD_OTP_USER) {
+		if (((mtd->writesize * otp_pages) - (from + len)) < 0)
+			return 0;
+	} else {
+		if (((mtd->writesize * otp_pages) - len) < 0)
+			return 0;
+	}

 	onenand_get_device(mtd, FL_OTPING);
 	while (len > 0 && otp_pages > 0) {
@@ -2749,13 +2964,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 			*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;
+			buf += tmp_retlen;
+			len -= tmp_retlen;
+			*retlen += tmp_retlen;

 			if (ret)
 				break;
@@ -2868,21 +3082,11 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
 	size_t retlen;
 	int ret;
+	unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;

 	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
 						 : mtd->oobsize);
 	/*
-	 * Note: OTP lock operation
-	 *       OTP block : 0xXXFC
-	 *       1st block : 0xXXF3 (If chip support)
-	 *       Both      : 0xXXF0 (If chip support)
-	 */
-	if (FLEXONENAND(this))
-		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
-	else
-		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.
 	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
@@ -2892,6 +3096,23 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	from = 0;
 	len = FLEXONENAND(this) ? mtd->writesize : 16;

+	/*
+	 * Note: OTP lock operation
+	 *       OTP block : 0xXXFC			XX 1111 1100
+	 *       1st block : 0xXXF3 (If chip support)	XX 1111 0011
+	 *       Both      : 0xXXF0 (If chip support)	XX 1111 0000
+	 */
+	if (FLEXONENAND(this))
+		otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
+
+#ifdef CONFIG_ONENAND_OTP_AREA
+	buf[otp_lock_offset] = 0xFC;
+#elif defined(CONFIG_ONENAND_OTP_BLOCK0)
+	buf[otp_lock_offset] = 0xF3;
+#elif defined(CONFIG_ONENAND_OTP_AREA_BLOCK0)
+	buf[otp_lock_offset] = 0xF0;
+#endif	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+
 	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);

 	return ret ? : retlen;
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 8ed8733..7bd1512 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -152,6 +152,8 @@ struct onenand_chip {
 /*
  * Helper macros
  */
+#define ONENAND_PAGES_PER_BLOCK        (1<<6)
+
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-02  5:59             ` Amul Kumar Saha
@ 2009-09-02  6:10               ` Artem Bityutskiy
  2009-09-03  5:51                 ` Amul Kumar Saha
  0 siblings, 1 reply; 27+ messages in thread
From: Artem Bityutskiy @ 2009-09-02  6:10 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: David Woodhouse, Kyungmin Park, linux-mtd

On 09/02/2009 08:59 AM, Amul Kumar Saha wrote:
> +if MTD_ONENAND_OTP
> +
> +config ONENAND_OTP_AREA
> +	bool "OTP area ONLY"
> +	depends on MTD_ONENAND_OTP
> +	select ON_OTP_AREA
> +
> +config ONENAND_OTP_BLOCK0
> +	bool "Block[0] ONLY"
> +	depends on MTD_ONENAND_OTP&&  !ONENAND_OTP_AREA
> +	select ON_OTP_BLOCK0
> +
> +config ONENAND_OTP_AREA_BLOCK0
> +	bool "BOTH OTP area AND Block[0]"
> +	depends on MTD_ONENAND_OTP&&  !ONENAND_OTP_AREA&&  !ONENAND_OTP_BLOCK0
> +	select ON_OTP_AREA_BLOCK0
> +
> +endif #MTD_ONENAND_OTP

If there were 10 OTP blocks, would you add 10 options?
I mean, are these switches really needed? Can we remove them?

> +
>   config MTD_ONENAND_2X_PROGRAM
>   	bool "OneNAND 2X program support"
>   	help
> diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
> index 6e82909..1b11a3e 100644
> --- a/drivers/mtd/onenand/onenand_base.c
> +++ b/drivers/mtd/onenand/onenand_base.c
> @@ -13,6 +13,10 @@
>    *	Flex-OneNAND support
>    *	Copyright (C) Samsung Electronics, 2008
>    *
> + *	Amul Kumar Saha<amul.saha@samsung.com>
> + *	OTP support
> + *	Copyright (C) SAMSUNG Electronics, 2009

If this all is (C) Samsung already, do you really need to add one more
(C) line?

> + *
>    * This program is free software; you can redistribute it and/or modify
>    * it under the terms of the GNU General Public License version 2 as
>    * published by the Free Software Foundation.
> @@ -308,6 +312,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
>   EXPORT_SYMBOL(flexonenand_region);
>
>   /**
> + * onenand_otp_command - Send OTP specific command to OneNAND device
> + * @param mtd	 MTD device structure
> + * @param cmd	 the command to be sent
> + * @param addr	 offset to read from or write to
> + * @param len	 number of bytes to read or write
> + */
> +static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
> +				size_t len)
> +{
> +	struct onenand_chip *this = mtd->priv;
> +	int value, block, page;
> +
> +	/* Address translation */
> +	switch (cmd) {
> +	case ONENAND_CMD_OTP_ACCESS:
> +		block = (int) (addr>>  this->erase_shift);

Why do you need the (int) cast there? How about cleaning up
>> (missing space) ?

> +		page = -1;
> +		break;
> +
> +	default:
> +		block = (int) (addr>>  this->erase_shift);
> +		page = (int) (addr>>  this->page_shift);

Ditto. And there are many places where you did not have put spaces properly.

> +/**
>    * onenand_command - [DEFAULT] Send command to OneNAND device
>    * @param mtd		MTD device structure
>    * @param cmd		the command to be sent
> @@ -1879,7 +1958,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
>   			onenand_update_bufferram(mtd, prev, !ret&&  !prev_subpage);
>   			if (ret) {
>   				written -= prevlen;
> -				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
> +				printk(KERN_ERR "onenand_write_ops_nolock: \
> +						write failed %d\n", ret);
>   				break;

Please, fix printk's. Do not use "\".

-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-02  6:10               ` Artem Bityutskiy
@ 2009-09-03  5:51                 ` Amul Kumar Saha
  2009-09-03  6:10                   ` Artem Bityutskiy
  0 siblings, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-09-03  5:51 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: Kyungmin Park, David Woodhouse, linux-mtd

Hi Artem,

>> +if MTD_ONENAND_OTP
>> +
>> +config ONENAND_OTP_AREA
>> + bool "OTP area ONLY"
>> + depends on MTD_ONENAND_OTP
>> + select ON_OTP_AREA
>> +
>> +config ONENAND_OTP_BLOCK0
>> + bool "Block[0] ONLY"
>> + depends on MTD_ONENAND_OTP&&  !ONENAND_OTP_AREA
>> + select ON_OTP_BLOCK0
>> +
>> +config ONENAND_OTP_AREA_BLOCK0
>> + bool "BOTH OTP area AND Block[0]"
>> + depends on MTD_ONENAND_OTP&&  !ONENAND_OTP_AREA&&  !ONENAND_OTP_BLOCK0
>> + select ON_OTP_AREA_BLOCK0
>> +
>> +endif #MTD_ONENAND_OTP
>
> If there were 10 OTP blocks, would you add 10 options?
> I mean, are these switches really needed? Can we remove them?

There is just one OTP block.
Three options are provided for the three known combinations of 1st Block and the OTP Block.
The option to choose one should be provided to the user.


>> @@ -13,6 +13,10 @@
>>    * Flex-OneNAND support
>>    * Copyright (C) Samsung Electronics, 2008
>>    *
>> + * Amul Kumar Saha<amul.saha@samsung.com>
>> + * OTP support
>> + * Copyright (C) SAMSUNG Electronics, 2009
>
> If this all is (C) Samsung already, do you really need to add one more
> (C) line?

Accepted

>> + struct onenand_chip *this = mtd->priv;
>> + int value, block, page;
>> +
>> + /* Address translation */
>> + switch (cmd) {
>> + case ONENAND_CMD_OTP_ACCESS:
>> + block = (int) (addr>>  this->erase_shift);
>
> Why do you need the (int) cast there? How about cleaning up
>>> (missing space) ?

I can do that, but (int) cast has been followed throughout onenand_base.c file
Just adhering to that.

>> + page = -1;
>> + break;
>> +
>> + default:
>> + block = (int) (addr>>  this->erase_shift);
>> + page = (int) (addr>>  this->page_shift);
>
> Ditto. And there are many places where you did not have put spaces properly.

I can't see any in the patch I sent.

>>   written -= prevlen;
>> - printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
>> + printk(KERN_ERR "onenand_write_ops_nolock: \
>> + write failed %d\n", ret);
>>   break;
>
> Please, fix printk's. Do not use "\".
>

Accepted

With Regards,
Amul Kumar Saha 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-03  5:51                 ` Amul Kumar Saha
@ 2009-09-03  6:10                   ` Artem Bityutskiy
  2009-09-03 10:37                     ` Amul Kumar Saha
  0 siblings, 1 reply; 27+ messages in thread
From: Artem Bityutskiy @ 2009-09-03  6:10 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: Kyungmin Park, David Woodhouse, linux-mtd

On Thu, 2009-09-03 at 11:21 +0530, Amul Kumar Saha wrote:
> Hi Artem,
> 
> >> +if MTD_ONENAND_OTP
> >> +
> >> +config ONENAND_OTP_AREA
> >> + bool "OTP area ONLY"
> >> + depends on MTD_ONENAND_OTP
> >> + select ON_OTP_AREA
> >> +
> >> +config ONENAND_OTP_BLOCK0
> >> + bool "Block[0] ONLY"
> >> + depends on MTD_ONENAND_OTP&&  !ONENAND_OTP_AREA
> >> + select ON_OTP_BLOCK0
> >> +
> >> +config ONENAND_OTP_AREA_BLOCK0
> >> + bool "BOTH OTP area AND Block[0]"
> >> + depends on MTD_ONENAND_OTP&&  !ONENAND_OTP_AREA&&  !ONENAND_OTP_BLOCK0
> >> + select ON_OTP_AREA_BLOCK0
> >> +
> >> +endif #MTD_ONENAND_OTP
> >
> > If there were 10 OTP blocks, would you add 10 options?
> > I mean, are these switches really needed? Can we remove them?
> 
> There is just one OTP block.
> Three options are provided for the three known combinations of 1st Block and the OTP Block.
> The option to choose one should be provided to the user.

Wouldn't it be better to  make this run-time configurable? E.g., module
parameters? Too many config options are frowned upon usually.

> >> + struct onenand_chip *this = mtd->priv;
> >> + int value, block, page;
> >> +
> >> + /* Address translation */
> >> + switch (cmd) {
> >> + case ONENAND_CMD_OTP_ACCESS:
> >> + block = (int) (addr>>  this->erase_shift);
> >
> > Why do you need the (int) cast there? How about cleaning up
> >>> (missing space) ?
> 
> I can do that, but (int) cast has been followed throughout onenand_base.c file
> Just adhering to that.

Ok, you could send a cleanup patch for all places.

> 
> >> + page = -1;
> >> + break;
> >> +
> >> + default:
> >> + block = (int) (addr>>  this->erase_shift);
> >> + page = (int) (addr>>  this->page_shift);
> >
> > Ditto. And there are many places where you did not have put spaces properly.
> 
> I can't see any in the patch I sent.

Sorry, this is my stupid Thunderbug, probably.

-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-03  6:10                   ` Artem Bityutskiy
@ 2009-09-03 10:37                     ` Amul Kumar Saha
  2009-09-03 11:19                       ` Artem Bityutskiy
  0 siblings, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-09-03 10:37 UTC (permalink / raw)
  To: dedekind1; +Cc: David Woodhouse, Kyungmin Park, linux-mtd

>> >> +
>> >> +config ONENAND_OTP_AREA_BLOCK0
>> >> + bool "BOTH OTP area AND Block[0]"
>> >> + depends on MTD_ONENAND_OTP&&  !ONENAND_OTP_AREA&&  !ONENAND_OTP_BLOCK0
>> >> + select ON_OTP_AREA_BLOCK0
>> >> +
>> >> +endif #MTD_ONENAND_OTP
>> >
>> > If there were 10 OTP blocks, would you add 10 options?
>> > I mean, are these switches really needed? Can we remove them?
>>
>> There is just one OTP block.
>> Three options are provided for the three known combinations of 1st Block and the OTP Block.
>> The option to choose one should be provided to the user.
>
> Wouldn't it be better to  make this run-time configurable? E.g., module
> parameters? Too many config options are frowned upon usually.
>

I got it. But I guess in this case the numbers are not that big enough to call for Module parameters 
implementation.
Is it okay?

With Regards,
Amul Kumar Saha 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-03 10:37                     ` Amul Kumar Saha
@ 2009-09-03 11:19                       ` Artem Bityutskiy
  2009-09-07  9:45                         ` Amul Kumar Saha
  0 siblings, 1 reply; 27+ messages in thread
From: Artem Bityutskiy @ 2009-09-03 11:19 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: David Woodhouse, Kyungmin Park, linux-mtd

On Thu, 2009-09-03 at 16:07 +0530, Amul Kumar Saha wrote:
> >> >> +
> >> >> +config ONENAND_OTP_AREA_BLOCK0
> >> >> + bool "BOTH OTP area AND Block[0]"
> >> >> + depends on MTD_ONENAND_OTP&&  !ONENAND_OTP_AREA&&  !ONENAND_OTP_BLOCK0
> >> >> + select ON_OTP_AREA_BLOCK0
> >> >> +
> >> >> +endif #MTD_ONENAND_OTP
> >> >
> >> > If there were 10 OTP blocks, would you add 10 options?
> >> > I mean, are these switches really needed? Can we remove them?
> >>
> >> There is just one OTP block.
> >> Three options are provided for the three known combinations of 1st Block and the OTP Block.
> >> The option to choose one should be provided to the user.
> >
> > Wouldn't it be better to  make this run-time configurable? E.g., module
> > parameters? Too many config options are frowned upon usually.
> >
> 
> I got it. But I guess in this case the numbers are not that big enough to call for Module parameters 
> implementation.
> Is it okay?

IMO, the amount of OneNAND config options is already large, and I would
not introduce more.

With module parameters you may always add kernel boot options like:

onenand.otpblk=0

or something like this.

-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-03 11:19                       ` Artem Bityutskiy
@ 2009-09-07  9:45                         ` Amul Kumar Saha
  2009-09-07  9:57                           ` Kyungmin Park
  2009-09-28  9:38                           ` Artem Bityutskiy
  0 siblings, 2 replies; 27+ messages in thread
From: Amul Kumar Saha @ 2009-09-07  9:45 UTC (permalink / raw)
  To: dedekind1; +Cc: David Woodhouse, Kyungmin Park, linux-mtd

What is OTP in OneNAND?
The device includes,
1. one block-sized OTP (One Time Programmable) area and
2. user-controlled 1st block OTP(Block 0)
that can be used to increase system security or to provide identification capabilities.

What is done?
In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0)
can be used as OTP area.
This area, available to the user, can be configured and locked with secured user information.
The OTP block can be read, programmed and locked using the same operations as any other NAND Flash
Array
memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed
to be a valid block.

Why it is done? (Impact)
Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental
re-programming of data stored in the 1st block and OTP Block.

Which problem it solves?
OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not
working with OneNAND devices.
Have observed the following in current OTP OneNAND Implmentation,
1. DataSheet specific sequence to lock the OTP Area is not followed.
2. Certain functions are quiet generic to cope with OTP specific activity.
This patch re-implements OTP support for OneNAND device.

How it is done?
For all blocks, 8th word is available to the user.
However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
Therefore, in case of OTP Block, user usage on this area is prohibited.
Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the
process of issuing an OTP-Lock.
The possible conditions are:-
1. Only 1st Block Lock
2. Only OTP Block Lock
3. Lock both the 1st Block and the OTP Block

What Other feature additions have been done in this patch?
This patch adds feature for:-
1. Only 1st Block Lock
2. Lock both the 1st Block and the OTP Blocks

Re-implemented OTP support for OneNAND
Added following features to OneNAND
	1. Lock only 1st Block in OneNAND
	2. Lock BOTH 1st Block and OTP Block in OneNAND

Signed-off-by: Amul Kumar Saha <amul.saha@samsung.com>
---
 drivers/mtd/onenand/onenand_base.c |  297 +++++++++++++++++++++++++++++++++----
 include/linux/mtd/onenand.h        |    2
 2 files changed, 267 insertions(+), 32 deletions(-)

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 6e82909..43a0963 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -11,7 +11,9 @@
  *
  *	Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
  *	Flex-OneNAND support
- *	Copyright (C) Samsung Electronics, 2008
+ *	Amul Kumar Saha <amul.saha@samsung.com>
+ *	OTP support
+ *	Copyright (C) Samsung Electronics, 2009
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -43,6 +45,18 @@ MODULE_PARM_DESC(flex_bdry,	"SLC Boundary information for Flex-OneNAND"
 				"    : 0->Set boundary in unlocked status"
 				"    : 1->Set boundary in locked status");

+/* Default OneNAND/Flex-OneNAND OTP options*/
+static int otp;
+
+module_param(otp, int, 0400);
+MODULE_PARM_DESC(otp,	"Corresponding behaviour of OneNAND in OTP"
+			"Syntax : otp=LOCK_TYPE"
+			"LOCK_TYPE : Keys issued, for specific OTP Lock type"
+			"	   : 0 -> Default (No Blocks Locked)"
+			"	   : 1 -> OTP Block lock"
+			"	   : 2 -> 1st Block lock"
+			"	   : 3 -> BOTH OTP Block and 1st Block lock");
+
 /**
  *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
  *  For now, we expose only 64 out of 80 ecc bytes
@@ -308,6 +322,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
 EXPORT_SYMBOL(flexonenand_region);

 /**
+ * onenand_otp_command - Send OTP specific command to OneNAND device
+ * @param mtd	 MTD device structure
+ * @param cmd	 the command to be sent
+ * @param addr	 offset to read from or write to
+ * @param len	 number of bytes to read or write
+ */
+static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
+				size_t len)
+{
+	struct onenand_chip *this = mtd->priv;
+	int value, block, page;
+
+	/* Address translation */
+	switch (cmd) {
+	case ONENAND_CMD_OTP_ACCESS:
+		block = (int) (addr >> this->erase_shift);
+		page = -1;
+		break;
+
+	default:
+		block = (int) (addr >> this->erase_shift);
+		page = (int) (addr >> this->page_shift);
+
+		if (ONENAND_IS_2PLANE(this)) {
+			/* Make the even block number */
+			block &= ~1;
+			/* Is it the odd plane? */
+			if (addr & this->writesize)
+				block++;
+			page >>= 1;
+		}
+		page &= this->page_mask;
+		break;
+	}
+
+	if (block != -1) {
+		/* Write 'DFS, FBA' of Flash */
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+	}
+
+	if (page != -1) {
+		/* Now we use page size operation */
+		int sectors = 4, count = 4;
+		int dataram;
+
+		switch (cmd) {
+		default:
+			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+				cmd = ONENAND_CMD_2X_PROG;
+			dataram = ONENAND_CURRENT_BUFFERRAM(this);
+			break;
+		}
+
+		/* Write 'FPA, FSA' of Flash */
+		value = onenand_page_address(page, sectors);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS8);
+
+		/* Write 'BSA, BSC' of DataRAM */
+		value = onenand_buffer_address(dataram, sectors, count);
+		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+	}
+
+	/* Interrupt clear */
+	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+	/* Write command */
+	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+	return 0;
+}
+
+/**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd		MTD device structure
  * @param cmd		the command to be sent
@@ -1879,7 +1968,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
 			if (ret) {
 				written -= prevlen;
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %d\n", ret);
 				break;
 			}

@@ -1905,14 +1995,16 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 			/* In partial page write we don't update bufferram */
 			onenand_update_bufferram(mtd, to, !ret && !subpage);
 			if (ret) {
-				printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						write failed %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);
+				printk(KERN_ERR "onenand_write_ops_nolock: \
+						verify failed %d\n", ret);
 				break;
 			}

@@ -1945,6 +2037,134 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,


 /**
+ * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * @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
+ *
+ * OneNAND write out-of-band only for OTP
+ */
+static int onenand_otp_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;
+	int block, value, status;
+
+	to += ops->ooboffs;
+
+	/* Initialize retlen, in case of early exit */
+	ops->oobretlen = 0;
+
+	oobsize = mtd->oobsize;
+
+	column = to & (mtd->oobsize - 1);
+
+	oobbuf = this->oob_buf;
+
+	/* Loop until all data write */
+	while (written < len) {
+		int thislen = min_t(int, oobsize, len - written);
+
+		cond_resched();
+
+		block = (int) (to >> this->erase_shift);
+		/*
+		 * Write 'DFS, FBA' of Flash
+		 * Add: F100h DQ=DFS, FBA
+		 */
+
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+
+		/*
+		 * Select DataRAM for DDP
+		 * Add: F101h DQ=DBS
+		 */
+
+		value = onenand_bufferram_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS2);
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+
+		/*
+		 * Enter OTP access mode
+		 */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
+
+		/* We send data to spare ram with oobsize
+		 * to prevent byte access */
+		memcpy(oobbuf + column, buf, thislen);
+
+		/*
+		 * Write Data into DataRAM
+		 * Add: 8th Word
+		 * in sector0/spare/page0
+		 * DQ=XXFCh
+		 */
+		this->write_bufferram(mtd, ONENAND_SPARERAM,
+					oobbuf, 0, mtd->oobsize);
+
+		onenand_otp_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;
+		}
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
+
+		status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+		status &= 0x60;
+
+		if (status == 0x60) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else if (status == 0x20) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
+		} else if (status == 0x40) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else {
+			printk(KERN_DEBUG "Reboot to check\n");
+		}
+
+		written += thislen;
+		if (written == len)
+			break;
+
+		to += mtd->writesize;
+		buf += thislen;
+		column = 0;
+	}
+
+	ops->oobretlen = written;
+
+	return ret;
+}
+
+/**
  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
  * @param mtd		MTD device structure
  * @param to		offset to write to
@@ -2659,11 +2879,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 	struct mtd_oob_ops ops;
 	int ret;

-	/* Enter OTP access mode */
-	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
-	this->wait(mtd, FL_OTPING);
-
 	if (FLEXONENAND(this)) {
+
+		/* Enter OTP access mode */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
 		/*
 		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
 		 * main area of page 49.
@@ -2674,19 +2894,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 		ops.oobbuf = NULL;
 		ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
 		*retlen = ops.retlen;
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
 	} else {
 		ops.mode = MTD_OOB_PLACE;
 		ops.ooblen = len;
 		ops.oobbuf = buf;
 		ops.ooboffs = 0;
-		ret = onenand_write_oob_nolock(mtd, from, &ops);
+		ret = onenand_otp_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;
 }

@@ -2717,16 +2937,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 	if (density < ONENAND_DEVICE_DENSITY_512Mb)
 		otp_pages = 20;
 	else
-		otp_pages = 10;
+		otp_pages = 50;

 	if (mode == MTD_OTP_FACTORY) {
 		from += mtd->writesize * otp_pages;
-		otp_pages = 64 - otp_pages;
+		otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
 	}

 	/* Check User/Factory boundary */
-	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
-		return 0;
+	if (mode == MTD_OTP_USER) {
+		if (((mtd->writesize * otp_pages) - (from + len)) < 0)
+			return 0;
+	} else {
+		if (((mtd->writesize * otp_pages) - len) < 0)
+			return 0;
+	}

 	onenand_get_device(mtd, FL_OTPING);
 	while (len > 0 && otp_pages > 0) {
@@ -2749,13 +2974,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 			*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;
+			buf += tmp_retlen;
+			len -= tmp_retlen;
+			*retlen += tmp_retlen;

 			if (ret)
 				break;
@@ -2868,21 +3092,11 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
 	size_t retlen;
 	int ret;
+	unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;

 	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
 						 : mtd->oobsize);
 	/*
-	 * Note: OTP lock operation
-	 *       OTP block : 0xXXFC
-	 *       1st block : 0xXXF3 (If chip support)
-	 *       Both      : 0xXXF0 (If chip support)
-	 */
-	if (FLEXONENAND(this))
-		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
-	else
-		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.
 	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
@@ -2892,6 +3106,25 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	from = 0;
 	len = FLEXONENAND(this) ? mtd->writesize : 16;

+	/*
+	 * Note: OTP lock operation
+	 *       OTP block : 0xXXFC			XX 1111 1100
+	 *       1st block : 0xXXF3 (If chip support)	XX 1111 0011
+	 *       Both      : 0xXXF0 (If chip support)	XX 1111 0000
+	 */
+	if (FLEXONENAND(this))
+		otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
+
+	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	if (otp == 1)
+		buf[otp_lock_offset] = 0xFC;
+	else if (otp == 2)
+		buf[otp_lock_offset] = 0xF3;
+	else if (otp == 3)
+		buf[otp_lock_offset] = 0xF0;
+	else if (otp != 0)
+		printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
+
 	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);

 	return ret ? : retlen;
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 8ed8733..7bd1512 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -152,6 +152,8 @@ struct onenand_chip {
 /*
  * Helper macros
  */
+#define ONENAND_PAGES_PER_BLOCK        (1<<6)
+
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-07  9:45                         ` Amul Kumar Saha
@ 2009-09-07  9:57                           ` Kyungmin Park
  2009-09-16  3:33                             ` Amul Kumar Saha
  2009-09-28  9:38                           ` Artem Bityutskiy
  1 sibling, 1 reply; 27+ messages in thread
From: Kyungmin Park @ 2009-09-07  9:57 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: linux-mtd, David Woodhouse, dedekind1

Hi,

Even though the acutal number at otp are used.  others are looks good to me.

Artem/Adrian, how do you think?

Thank you,
Kyungmin Park

On Mon, Sep 7, 2009 at 6:45 PM, Amul Kumar Saha<amul.saha@samsung.com> wrote:
> What is OTP in OneNAND?
> The device includes,
> 1. one block-sized OTP (One Time Programmable) area and
> 2. user-controlled 1st block OTP(Block 0)
> that can be used to increase system security or to provide identification capabilities.
>
> What is done?
> In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0)
> can be used as OTP area.
> This area, available to the user, can be configured and locked with secured user information.
> The OTP block can be read, programmed and locked using the same operations as any other NAND Flash
> Array
> memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed
> to be a valid block.
>
> Why it is done? (Impact)
> Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental
> re-programming of data stored in the 1st block and OTP Block.
>
> Which problem it solves?
> OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not
> working with OneNAND devices.
> Have observed the following in current OTP OneNAND Implmentation,
> 1. DataSheet specific sequence to lock the OTP Area is not followed.
> 2. Certain functions are quiet generic to cope with OTP specific activity.
> This patch re-implements OTP support for OneNAND device.
>
> How it is done?
> For all blocks, 8th word is available to the user.
> However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
> Therefore, in case of OTP Block, user usage on this area is prohibited.
> Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the
> process of issuing an OTP-Lock.
> The possible conditions are:-
> 1. Only 1st Block Lock
> 2. Only OTP Block Lock
> 3. Lock both the 1st Block and the OTP Block
>
> What Other feature additions have been done in this patch?
> This patch adds feature for:-
> 1. Only 1st Block Lock
> 2. Lock both the 1st Block and the OTP Blocks
>
> Re-implemented OTP support for OneNAND
> Added following features to OneNAND
>        1. Lock only 1st Block in OneNAND
>        2. Lock BOTH 1st Block and OTP Block in OneNAND
>
> Signed-off-by: Amul Kumar Saha <amul.saha@samsung.com>
> ---
>  drivers/mtd/onenand/onenand_base.c |  297 +++++++++++++++++++++++++++++++++----
>  include/linux/mtd/onenand.h        |    2
>  2 files changed, 267 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
> index 6e82909..43a0963 100644
> --- a/drivers/mtd/onenand/onenand_base.c
> +++ b/drivers/mtd/onenand/onenand_base.c
> @@ -11,7 +11,9 @@
>  *
>  *     Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
>  *     Flex-OneNAND support
> - *     Copyright (C) Samsung Electronics, 2008
> + *     Amul Kumar Saha <amul.saha@samsung.com>
> + *     OTP support
> + *     Copyright (C) Samsung Electronics, 2009
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License version 2 as
> @@ -43,6 +45,18 @@ MODULE_PARM_DESC(flex_bdry,  "SLC Boundary information for Flex-OneNAND"
>                                "    : 0->Set boundary in unlocked status"
>                                "    : 1->Set boundary in locked status");
>
> +/* Default OneNAND/Flex-OneNAND OTP options*/
> +static int otp;
> +
> +module_param(otp, int, 0400);
> +MODULE_PARM_DESC(otp,  "Corresponding behaviour of OneNAND in OTP"
> +                       "Syntax : otp=LOCK_TYPE"
> +                       "LOCK_TYPE : Keys issued, for specific OTP Lock type"
> +                       "          : 0 -> Default (No Blocks Locked)"
> +                       "          : 1 -> OTP Block lock"
> +                       "          : 2 -> 1st Block lock"
> +                       "          : 3 -> BOTH OTP Block and 1st Block lock");
> +
>  /**
>  *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
>  *  For now, we expose only 64 out of 80 ecc bytes
> @@ -308,6 +322,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
>  EXPORT_SYMBOL(flexonenand_region);
>
>  /**
> + * onenand_otp_command - Send OTP specific command to OneNAND device
> + * @param mtd   MTD device structure
> + * @param cmd   the command to be sent
> + * @param addr  offset to read from or write to
> + * @param len   number of bytes to read or write
> + */
> +static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
> +                               size_t len)
> +{
> +       struct onenand_chip *this = mtd->priv;
> +       int value, block, page;
> +
> +       /* Address translation */
> +       switch (cmd) {
> +       case ONENAND_CMD_OTP_ACCESS:
> +               block = (int) (addr >> this->erase_shift);
> +               page = -1;
> +               break;
> +
> +       default:
> +               block = (int) (addr >> this->erase_shift);
> +               page = (int) (addr >> this->page_shift);
> +
> +               if (ONENAND_IS_2PLANE(this)) {
> +                       /* Make the even block number */
> +                       block &= ~1;
> +                       /* Is it the odd plane? */
> +                       if (addr & this->writesize)
> +                               block++;
> +                       page >>= 1;
> +               }
> +               page &= this->page_mask;
> +               break;
> +       }
> +
> +       if (block != -1) {
> +               /* Write 'DFS, FBA' of Flash */
> +               value = onenand_block_address(this, block);
> +               this->write_word(value, this->base +
> +                               ONENAND_REG_START_ADDRESS1);
> +       }
> +
> +       if (page != -1) {
> +               /* Now we use page size operation */
> +               int sectors = 4, count = 4;
> +               int dataram;
> +
> +               switch (cmd) {
> +               default:
> +                       if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
> +                               cmd = ONENAND_CMD_2X_PROG;
> +                       dataram = ONENAND_CURRENT_BUFFERRAM(this);
> +                       break;
> +               }
> +
> +               /* Write 'FPA, FSA' of Flash */
> +               value = onenand_page_address(page, sectors);
> +               this->write_word(value, this->base +
> +                               ONENAND_REG_START_ADDRESS8);
> +
> +               /* Write 'BSA, BSC' of DataRAM */
> +               value = onenand_buffer_address(dataram, sectors, count);
> +               this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
> +       }
> +
> +       /* Interrupt clear */
> +       this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
> +
> +       /* Write command */
> +       this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
> +
> +       return 0;
> +}
> +
> +/**
>  * onenand_command - [DEFAULT] Send command to OneNAND device
>  * @param mtd          MTD device structure
>  * @param cmd          the command to be sent
> @@ -1879,7 +1968,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
>                        onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
>                        if (ret) {
>                                written -= prevlen;
> -                               printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
> +                               printk(KERN_ERR "onenand_write_ops_nolock: \
> +                                               write failed %d\n", ret);
>                                break;
>                        }
>
> @@ -1905,14 +1995,16 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
>                        /* In partial page write we don't update bufferram */
>                        onenand_update_bufferram(mtd, to, !ret && !subpage);
>                        if (ret) {
> -                               printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
> +                               printk(KERN_ERR "onenand_write_ops_nolock: \
> +                                               write failed %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);
> +                               printk(KERN_ERR "onenand_write_ops_nolock: \
> +                                               verify failed %d\n", ret);
>                                break;
>                        }
>
> @@ -1945,6 +2037,134 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
>
>
>  /**
> + * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
> + * @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
> + *
> + * OneNAND write out-of-band only for OTP
> + */
> +static int onenand_otp_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;
> +       int block, value, status;
> +
> +       to += ops->ooboffs;
> +
> +       /* Initialize retlen, in case of early exit */
> +       ops->oobretlen = 0;
> +
> +       oobsize = mtd->oobsize;
> +
> +       column = to & (mtd->oobsize - 1);
> +
> +       oobbuf = this->oob_buf;
> +
> +       /* Loop until all data write */
> +       while (written < len) {
> +               int thislen = min_t(int, oobsize, len - written);
> +
> +               cond_resched();
> +
> +               block = (int) (to >> this->erase_shift);
> +               /*
> +                * Write 'DFS, FBA' of Flash
> +                * Add: F100h DQ=DFS, FBA
> +                */
> +
> +               value = onenand_block_address(this, block);
> +               this->write_word(value, this->base +
> +                               ONENAND_REG_START_ADDRESS1);
> +
> +               /*
> +                * Select DataRAM for DDP
> +                * Add: F101h DQ=DBS
> +                */
> +
> +               value = onenand_bufferram_address(this, block);
> +               this->write_word(value, this->base +
> +                               ONENAND_REG_START_ADDRESS2);
> +               ONENAND_SET_NEXT_BUFFERRAM(this);
> +
> +               /*
> +                * Enter OTP access mode
> +                */
> +               this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
> +               this->wait(mtd, FL_OTPING);
> +
> +               /* We send data to spare ram with oobsize
> +                * to prevent byte access */
> +               memcpy(oobbuf + column, buf, thislen);
> +
> +               /*
> +                * Write Data into DataRAM
> +                * Add: 8th Word
> +                * in sector0/spare/page0
> +                * DQ=XXFCh
> +                */
> +               this->write_bufferram(mtd, ONENAND_SPARERAM,
> +                                       oobbuf, 0, mtd->oobsize);
> +
> +               onenand_otp_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;
> +               }
> +
> +               /* Exit OTP access mode */
> +               this->command(mtd, ONENAND_CMD_RESET, 0, 0);
> +               this->wait(mtd, FL_RESETING);
> +
> +               status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
> +               status &= 0x60;
> +
> +               if (status == 0x60) {
> +                       printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
> +                       printk(KERN_DEBUG "1st Block\tLOCKED\n");
> +                       printk(KERN_DEBUG "OTP Block\tLOCKED\n");
> +               } else if (status == 0x20) {
> +                       printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
> +                       printk(KERN_DEBUG "1st Block\tLOCKED\n");
> +                       printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
> +               } else if (status == 0x40) {
> +                       printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
> +                       printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
> +                       printk(KERN_DEBUG "OTP Block\tLOCKED\n");
> +               } else {
> +                       printk(KERN_DEBUG "Reboot to check\n");
> +               }
> +
> +               written += thislen;
> +               if (written == len)
> +                       break;
> +
> +               to += mtd->writesize;
> +               buf += thislen;
> +               column = 0;
> +       }
> +
> +       ops->oobretlen = written;
> +
> +       return ret;
> +}
> +
> +/**
>  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
>  * @param mtd          MTD device structure
>  * @param to           offset to write to
> @@ -2659,11 +2879,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
>        struct mtd_oob_ops ops;
>        int ret;
>
> -       /* Enter OTP access mode */
> -       this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
> -       this->wait(mtd, FL_OTPING);
> -
>        if (FLEXONENAND(this)) {
> +
> +               /* Enter OTP access mode */
> +               this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
> +               this->wait(mtd, FL_OTPING);
>                /*
>                 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
>                 * main area of page 49.
> @@ -2674,19 +2894,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
>                ops.oobbuf = NULL;
>                ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
>                *retlen = ops.retlen;
> +
> +               /* Exit OTP access mode */
> +               this->command(mtd, ONENAND_CMD_RESET, 0, 0);
> +               this->wait(mtd, FL_RESETING);
>        } else {
>                ops.mode = MTD_OOB_PLACE;
>                ops.ooblen = len;
>                ops.oobbuf = buf;
>                ops.ooboffs = 0;
> -               ret = onenand_write_oob_nolock(mtd, from, &ops);
> +               ret = onenand_otp_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;
>  }
>
> @@ -2717,16 +2937,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
>        if (density < ONENAND_DEVICE_DENSITY_512Mb)
>                otp_pages = 20;
>        else
> -               otp_pages = 10;
> +               otp_pages = 50;
>
>        if (mode == MTD_OTP_FACTORY) {
>                from += mtd->writesize * otp_pages;
> -               otp_pages = 64 - otp_pages;
> +               otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
>        }
>
>        /* Check User/Factory boundary */
> -       if (((mtd->writesize * otp_pages) - (from + len)) < 0)
> -               return 0;
> +       if (mode == MTD_OTP_USER) {
> +               if (((mtd->writesize * otp_pages) - (from + len)) < 0)
> +                       return 0;
> +       } else {
> +               if (((mtd->writesize * otp_pages) - len) < 0)
> +                       return 0;
> +       }
>
>        onenand_get_device(mtd, FL_OTPING);
>        while (len > 0 && otp_pages > 0) {
> @@ -2749,13 +2974,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
>                        *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;
> +                       buf += tmp_retlen;
> +                       len -= tmp_retlen;
> +                       *retlen += tmp_retlen;
>
>                        if (ret)
>                                break;
> @@ -2868,21 +3092,11 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
>        u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
>        size_t retlen;
>        int ret;
> +       unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;
>
>        memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
>                                                 : mtd->oobsize);
>        /*
> -        * Note: OTP lock operation
> -        *       OTP block : 0xXXFC
> -        *       1st block : 0xXXF3 (If chip support)
> -        *       Both      : 0xXXF0 (If chip support)
> -        */
> -       if (FLEXONENAND(this))
> -               buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
> -       else
> -               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.
>         * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
> @@ -2892,6 +3106,25 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
>        from = 0;
>        len = FLEXONENAND(this) ? mtd->writesize : 16;
>
> +       /*
> +        * Note: OTP lock operation
> +        *       OTP block : 0xXXFC                     XX 1111 1100
> +        *       1st block : 0xXXF3 (If chip support)   XX 1111 0011
> +        *       Both      : 0xXXF0 (If chip support)   XX 1111 0000
> +        */
> +       if (FLEXONENAND(this))
> +               otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
> +
> +       /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
> +       if (otp == 1)
> +               buf[otp_lock_offset] = 0xFC;
> +       else if (otp == 2)
> +               buf[otp_lock_offset] = 0xF3;
> +       else if (otp == 3)
> +               buf[otp_lock_offset] = 0xF0;
> +       else if (otp != 0)
> +               printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
> +
>        ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
>
>        return ret ? : retlen;
> diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
> index 8ed8733..7bd1512 100644
> --- a/include/linux/mtd/onenand.h
> +++ b/include/linux/mtd/onenand.h
> @@ -152,6 +152,8 @@ struct onenand_chip {
>  /*
>  * Helper macros
>  */
> +#define ONENAND_PAGES_PER_BLOCK        (1<<6)
> +
>  #define ONENAND_CURRENT_BUFFERRAM(this)                (this->bufferram_index)
>  #define ONENAND_NEXT_BUFFERRAM(this)           (this->bufferram_index ^ 1)
>  #define ONENAND_SET_NEXT_BUFFERRAM(this)       (this->bufferram_index ^= 1)
>
>
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-07  9:57                           ` Kyungmin Park
@ 2009-09-16  3:33                             ` Amul Kumar Saha
  2009-09-16  6:40                               ` Artem Bityutskiy
  0 siblings, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-09-16  3:33 UTC (permalink / raw)
  To: Kyungmin Park; +Cc: David Woodhouse, linux-mtd, dedekind1

Hi Artem,

> Even though the acutal number at otp are used.  others are looks good to me.
>
> Artem/Adrian, how do you think?
>

Awaiting your comments.

Regards,
Amul Kumar Saha 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-16  3:33                             ` Amul Kumar Saha
@ 2009-09-16  6:40                               ` Artem Bityutskiy
  0 siblings, 0 replies; 27+ messages in thread
From: Artem Bityutskiy @ 2009-09-16  6:40 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: linux-mtd, Kyungmin Park, David Woodhouse

On 09/16/2009 06:33 AM, Amul Kumar Saha wrote:
> Hi Artem,
>
>> Even though the acutal number at otp are used.  others are looks good to me.
>>
>> Artem/Adrian, how do you think?
>>
>
> Awaiting your comments.

I think Adrian was going to review this, as well as multi-block
erase patches from Mika. I guess he is just a little busy.

-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-07  9:45                         ` Amul Kumar Saha
  2009-09-07  9:57                           ` Kyungmin Park
@ 2009-09-28  9:38                           ` Artem Bityutskiy
  2009-10-01  6:48                             ` Amul Kumar Saha
  2009-10-12  4:42                             ` Amul Kumar Saha
  1 sibling, 2 replies; 27+ messages in thread
From: Artem Bityutskiy @ 2009-09-28  9:38 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: David Woodhouse, Kyungmin Park, linux-mtd, adrian.hunter

On Mon, 2009-09-07 at 15:15 +0530, Amul Kumar Saha wrote:
> What is OTP in OneNAND?
> The device includes,
> 1. one block-sized OTP (One Time Programmable) area and
> 2. user-controlled 1st block OTP(Block 0)
> that can be used to increase system security or to provide identification capabilities.
> 

Could you please refresh your patch and resend it? The old one does not
apply anymore.

Also, could you please avoid doing these:

printk(KERN_ERR "onenand_write_ops_nolock: \
       write failed %d\n", ret);

function name prints. This is bad practice. You could amend whole
onenand_base.c first, then send the OTP re-write as a follow up patch.

-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-28  9:38                           ` Artem Bityutskiy
@ 2009-10-01  6:48                             ` Amul Kumar Saha
  2009-10-12  4:42                             ` Amul Kumar Saha
  1 sibling, 0 replies; 27+ messages in thread
From: Amul Kumar Saha @ 2009-10-01  6:48 UTC (permalink / raw)
  To: dedekind1; +Cc: Kyungmin Park, David Woodhouse, linux-mtd, adrian.hunter

Hi Artem,

> Could you please refresh your patch and resend it? The old one does not
> apply anymore.
>
> Also, could you please avoid doing these:
>
> printk(KERN_ERR "onenand_write_ops_nolock: \
>       write failed %d\n", ret);
>
> function name prints. This is bad practice. You could amend whole
> onenand_base.c first, then send the OTP re-write as a follow up patch.

I understand.
Sending the patch.

Regards,
Amul Kumar Saha 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-09-28  9:38                           ` Artem Bityutskiy
  2009-10-01  6:48                             ` Amul Kumar Saha
@ 2009-10-12  4:42                             ` Amul Kumar Saha
  1 sibling, 0 replies; 27+ messages in thread
From: Amul Kumar Saha @ 2009-10-12  4:42 UTC (permalink / raw)
  To: dedekind1; +Cc: Kyungmin Park, David Woodhouse, linux-mtd, adrian.hunter

Hi Artem,

> Could you please refresh your patch and resend it? The old one does not
> apply anymore.
>
> Also, could you please avoid doing these:
>
> printk(KERN_ERR "onenand_write_ops_nolock: \
>       write failed %d\n", ret);
>
> function name prints. This is bad practice. You could amend whole
> onenand_base.c first, then send the OTP re-write as a follow up patch.
>

As you have requested, rebased the OTP patch to the current version.
Sending the OTP patch.

Regards,
Amul 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
@ 2009-10-12  6:01 Amul Kumar Saha
  2009-10-20  8:56 ` Artem Bityutskiy
  0 siblings, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-10-12  6:01 UTC (permalink / raw)
  To: dedekind1; +Cc: Kyungmin Park, David Woodhouse, linux-mtd, adrian.hunter

What is OTP in OneNAND?
The device includes,
1. one block-sized OTP (One Time Programmable) area and
2. user-controlled 1st block OTP(Block 0)
that can be used to increase system security or to provide identification capabilities.

What is done?
In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0)
can be used as OTP area.
This area, available to the user, can be configured and locked with secured user information.
The OTP block can be read, programmed and locked using the same operations as any other NAND Flash
Array
memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed
to be a valid block.

Why it is done? (Impact)
Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental
re-programming of data stored in the 1st block and OTP Block.

Which problem it solves?
OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not
working with OneNAND devices.
Have observed the following in current OTP OneNAND Implmentation,
1. DataSheet specific sequence to lock the OTP Area is not followed.
2. Certain functions are quiet generic to cope with OTP specific activity.
This patch re-implements OTP support for OneNAND device.

How it is done?
For all blocks, 8th word is available to the user.
However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
Therefore, in case of OTP Block, user usage on this area is prohibited.
Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the
process of issuing an OTP-Lock.
The possible conditions are:-
1. Only 1st Block Lock
2. Only OTP Block Lock
3. Lock both the 1st Block and the OTP Block

What Other feature additions have been done in this patch?
This patch adds feature for:-
1. Only 1st Block Lock
2. Lock both the 1st Block and the OTP Blocks

Re-implemented OTP support for OneNAND
Added following features to OneNAND
	1. Lock only 1st Block in OneNAND
	2. Lock BOTH 1st Block and OTP Block in OneNAND

Signed-off-by: Amul Kumar Saha <amul.saha at samsung.com>
---
 drivers/mtd/onenand/onenand_base.c |  287 +++++++++++++++++++++++++++++++++----
 include/linux/mtd/onenand.h        |    2
 2 files changed, 260 insertions(+), 29 deletions(-)

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index cb405bc..4d28268 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -11,7 +11,9 @@
  *
  *	Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
  *	Flex-OneNAND support
- *	Copyright (C) Samsung Electronics, 2008
+ *	Amul Kumar Saha <amul.saha at samsung.com>
+ *	OTP support
+ *	Copyright (C) Samsung Electronics, 2009
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -43,6 +45,18 @@ MODULE_PARM_DESC(flex_bdry,	"SLC Boundary information for Flex-OneNAND"
 				"    : 0->Set boundary in unlocked status"
 				"    : 1->Set boundary in locked status");

+/* Default OneNAND/Flex-OneNAND OTP options*/
+static int otp;
+
+module_param(otp, int, 0400);
+MODULE_PARM_DESC(otp,	"Corresponding behaviour of OneNAND in OTP"
+			"Syntax : otp=LOCK_TYPE"
+			"LOCK_TYPE : Keys issued, for specific OTP Lock type"
+			"	   : 0 -> Default (No Blocks Locked)"
+			"	   : 1 -> OTP Block lock"
+			"	   : 2 -> 1st Block lock"
+			"	   : 3 -> BOTH OTP Block and 1st Block lock");
+
 /**
  *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
  *  For now, we expose only 64 out of 80 ecc bytes
@@ -308,6 +322,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
 EXPORT_SYMBOL(flexonenand_region);

 /**
+ * onenand_otp_command - Send OTP specific command to OneNAND device
+ * @param mtd	 MTD device structure
+ * @param cmd	 the command to be sent
+ * @param addr	 offset to read from or write to
+ * @param len	 number of bytes to read or write
+ */
+static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
+				size_t len)
+{
+	struct onenand_chip *this = mtd->priv;
+	int value, block, page;
+
+	/* Address translation */
+	switch (cmd) {
+	case ONENAND_CMD_OTP_ACCESS:
+		block = (int) (addr >> this->erase_shift);
+		page = -1;
+		break;
+
+	default:
+		block = (int) (addr >> this->erase_shift);
+		page = (int) (addr >> this->page_shift);
+
+		if (ONENAND_IS_2PLANE(this)) {
+			/* Make the even block number */
+			block &= ~1;
+			/* Is it the odd plane? */
+			if (addr & this->writesize)
+				block++;
+			page >>= 1;
+		}
+		page &= this->page_mask;
+		break;
+	}
+
+	if (block != -1) {
+		/* Write 'DFS, FBA' of Flash */
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+	}
+
+	if (page != -1) {
+		/* Now we use page size operation */
+		int sectors = 4, count = 4;
+		int dataram;
+
+		switch (cmd) {
+		default:
+			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+				cmd = ONENAND_CMD_2X_PROG;
+			dataram = ONENAND_CURRENT_BUFFERRAM(this);
+			break;
+		}
+
+		/* Write 'FPA, FSA' of Flash */
+		value = onenand_page_address(page, sectors);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS8);
+
+		/* Write 'BSA, BSC' of DataRAM */
+		value = onenand_buffer_address(dataram, sectors, count);
+		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+	}
+
+	/* Interrupt clear */
+	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+	/* Write command */
+	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+	return 0;
+}
+
+/**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd		MTD device structure
  * @param cmd		the command to be sent
@@ -1969,6 +2058,133 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,


 /**
+ * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * @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
+ *
+ * OneNAND write out-of-band only for OTP
+ */
+static int onenand_otp_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;
+	int block, value, status;
+
+	to += ops->ooboffs;
+
+	/* Initialize retlen, in case of early exit */
+	ops->oobretlen = 0;
+
+	oobsize = mtd->oobsize;
+
+	column = to & (mtd->oobsize - 1);
+
+	oobbuf = this->oob_buf;
+
+	/* Loop until all data write */
+	while (written < len) {
+		int thislen = min_t(int, oobsize, len - written);
+
+		cond_resched();
+
+		block = (int) (to >> this->erase_shift);
+		/*
+		 * Write 'DFS, FBA' of Flash
+		 * Add: F100h DQ=DFS, FBA
+		 */
+
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+
+		/*
+		 * Select DataRAM for DDP
+		 * Add: F101h DQ=DBS
+		 */
+
+		value = onenand_bufferram_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS2);
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+
+		/*
+		 * Enter OTP access mode
+		 */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
+
+		/* We send data to spare ram with oobsize
+		 * to prevent byte access */
+		memcpy(oobbuf + column, buf, thislen);
+
+		/*
+		 * Write Data into DataRAM
+		 * Add: 8th Word
+		 * in sector0/spare/page0
+		 * DQ=XXFCh
+		 */
+		this->write_bufferram(mtd, ONENAND_SPARERAM,
+					oobbuf, 0, mtd->oobsize);
+
+		onenand_otp_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 "%s: write failed %d\n", __func__, ret);
+			break;
+		}
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
+
+		status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+		status &= 0x60;
+
+		if (status == 0x60) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else if (status == 0x20) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
+		} else if (status == 0x40) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else {
+			printk(KERN_DEBUG "Reboot to check\n");
+		}
+
+		written += thislen;
+		if (written == len)
+			break;
+
+		to += mtd->writesize;
+		buf += thislen;
+		column = 0;
+	}
+
+	ops->oobretlen = written;
+
+	return ret;
+}
+
+/**
  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
  * @param mtd		MTD device structure
  * @param to		offset to write to
@@ -2693,11 +2909,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 	struct mtd_oob_ops ops;
 	int ret;

-	/* Enter OTP access mode */
-	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
-	this->wait(mtd, FL_OTPING);
-
 	if (FLEXONENAND(this)) {
+
+		/* Enter OTP access mode */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
 		/*
 		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
 		 * main area of page 49.
@@ -2708,19 +2924,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 		ops.oobbuf = NULL;
 		ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
 		*retlen = ops.retlen;
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
 	} else {
 		ops.mode = MTD_OOB_PLACE;
 		ops.ooblen = len;
 		ops.oobbuf = buf;
 		ops.ooboffs = 0;
-		ret = onenand_write_oob_nolock(mtd, from, &ops);
+		ret = onenand_otp_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;
 }

@@ -2751,16 +2967,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 	if (density < ONENAND_DEVICE_DENSITY_512Mb)
 		otp_pages = 20;
 	else
-		otp_pages = 10;
+		otp_pages = 50;

 	if (mode == MTD_OTP_FACTORY) {
 		from += mtd->writesize * otp_pages;
-		otp_pages = 64 - otp_pages;
+		otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
 	}

 	/* Check User/Factory boundary */
-	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
-		return 0;
+	if (mode == MTD_OTP_USER) {
+		if (((mtd->writesize * otp_pages) - (from + len)) < 0)
+			return 0;
+	} else {
+		if (((mtd->writesize * otp_pages) - len) < 0)
+			return 0;
+	}

 	onenand_get_device(mtd, FL_OTPING);
 	while (len > 0 && otp_pages > 0) {
@@ -2783,13 +3004,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 			*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;
+			buf += tmp_retlen;
+			len -= tmp_retlen;
+			*retlen += tmp_retlen;

 			if (ret)
 				break;
@@ -2902,21 +3122,11 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
 	size_t retlen;
 	int ret;
+	unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;

 	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
 						 : mtd->oobsize);
 	/*
-	 * Note: OTP lock operation
-	 *       OTP block : 0xXXFC
-	 *       1st block : 0xXXF3 (If chip support)
-	 *       Both      : 0xXXF0 (If chip support)
-	 */
-	if (FLEXONENAND(this))
-		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
-	else
-		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.
 	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
@@ -2926,6 +3136,25 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	from = 0;
 	len = FLEXONENAND(this) ? mtd->writesize : 16;

+	/*
+	 * Note: OTP lock operation
+	 *       OTP block : 0xXXFC			XX 1111 1100
+	 *       1st block : 0xXXF3 (If chip support)	XX 1111 0011
+	 *       Both      : 0xXXF0 (If chip support)	XX 1111 0000
+	 */
+	if (FLEXONENAND(this))
+		otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
+
+	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	if (otp == 1)
+		buf[otp_lock_offset] = 0xFC;
+	else if (otp == 2)
+		buf[otp_lock_offset] = 0xF3;
+	else if (otp == 3)
+		buf[otp_lock_offset] = 0xF0;
+	else if (otp != 0)
+		printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
+
 	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);

 	return ret ? : retlen;
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 4e49f33..3c00b55 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -152,6 +152,8 @@ struct onenand_chip {
 /*
  * Helper macros
  */
+#define ONENAND_PAGES_PER_BLOCK        (1<<6)
+
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-10-12  6:01 Amul Kumar Saha
@ 2009-10-20  8:56 ` Artem Bityutskiy
  2009-10-20 14:13   ` Adrian Hunter
  0 siblings, 1 reply; 27+ messages in thread
From: Artem Bityutskiy @ 2009-10-20  8:56 UTC (permalink / raw)
  To: Amul Kumar Saha, Adrian Hunter
  Cc: Kyungmin Park, David Woodhouse, linux-mtd, adrian.hunter

I'm still waiting for Adrian to review this. Yesterday he said he will
do this soon. In any case, I remember about this patch, just FYI.

On Mon, 2009-10-12 at 11:31 +0530, Amul Kumar Saha wrote:
> What is OTP in OneNAND?
> The device includes,
> 1. one block-sized OTP (One Time Programmable) area and
> 2. user-controlled 1st block OTP(Block 0)
> that can be used to increase system security or to provide identification capabilities.
> 
> What is done?
> In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0)
> can be used as OTP area.
> This area, available to the user, can be configured and locked with secured user information.
> The OTP block can be read, programmed and locked using the same operations as any other NAND Flash
> Array
> memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed
> to be a valid block.
> 
> Why it is done? (Impact)
> Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental
> re-programming of data stored in the 1st block and OTP Block.
> 
> Which problem it solves?
> OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not
> working with OneNAND devices.
> Have observed the following in current OTP OneNAND Implmentation,
> 1. DataSheet specific sequence to lock the OTP Area is not followed.
> 2. Certain functions are quiet generic to cope with OTP specific activity.
> This patch re-implements OTP support for OneNAND device.
> 
> How it is done?
> For all blocks, 8th word is available to the user.
> However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
> Therefore, in case of OTP Block, user usage on this area is prohibited.
> Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the
> process of issuing an OTP-Lock.
> The possible conditions are:-
> 1. Only 1st Block Lock
> 2. Only OTP Block Lock
> 3. Lock both the 1st Block and the OTP Block
> 
> What Other feature additions have been done in this patch?
> This patch adds feature for:-
> 1. Only 1st Block Lock
> 2. Lock both the 1st Block and the OTP Blocks
> 
> Re-implemented OTP support for OneNAND
> Added following features to OneNAND
> 	1. Lock only 1st Block in OneNAND
> 	2. Lock BOTH 1st Block and OTP Block in OneNAND
> 
> Signed-off-by: Amul Kumar Saha <amul.saha at samsung.com>
> ---
>  drivers/mtd/onenand/onenand_base.c |  287 +++++++++++++++++++++++++++++++++----
>  include/linux/mtd/onenand.h        |    2
>  2 files changed, 260 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
> index cb405bc..4d28268 100644
> --- a/drivers/mtd/onenand/onenand_base.c
> +++ b/drivers/mtd/onenand/onenand_base.c
> @@ -11,7 +11,9 @@
>   *
>   *	Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
>   *	Flex-OneNAND support
> - *	Copyright (C) Samsung Electronics, 2008
> + *	Amul Kumar Saha <amul.saha at samsung.com>
> + *	OTP support
> + *	Copyright (C) Samsung Electronics, 2009
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License version 2 as
> @@ -43,6 +45,18 @@ MODULE_PARM_DESC(flex_bdry,	"SLC Boundary information for Flex-OneNAND"
>  				"    : 0->Set boundary in unlocked status"
>  				"    : 1->Set boundary in locked status");
> 
> +/* Default OneNAND/Flex-OneNAND OTP options*/
> +static int otp;
> +
> +module_param(otp, int, 0400);
> +MODULE_PARM_DESC(otp,	"Corresponding behaviour of OneNAND in OTP"
> +			"Syntax : otp=LOCK_TYPE"
> +			"LOCK_TYPE : Keys issued, for specific OTP Lock type"
> +			"	   : 0 -> Default (No Blocks Locked)"
> +			"	   : 1 -> OTP Block lock"
> +			"	   : 2 -> 1st Block lock"
> +			"	   : 3 -> BOTH OTP Block and 1st Block lock");
> +
>  /**
>   *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
>   *  For now, we expose only 64 out of 80 ecc bytes
> @@ -308,6 +322,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
>  EXPORT_SYMBOL(flexonenand_region);
> 
>  /**
> + * onenand_otp_command - Send OTP specific command to OneNAND device
> + * @param mtd	 MTD device structure
> + * @param cmd	 the command to be sent
> + * @param addr	 offset to read from or write to
> + * @param len	 number of bytes to read or write
> + */
> +static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
> +				size_t len)
> +{
> +	struct onenand_chip *this = mtd->priv;
> +	int value, block, page;
> +
> +	/* Address translation */
> +	switch (cmd) {
> +	case ONENAND_CMD_OTP_ACCESS:
> +		block = (int) (addr >> this->erase_shift);
> +		page = -1;
> +		break;
> +
> +	default:
> +		block = (int) (addr >> this->erase_shift);
> +		page = (int) (addr >> this->page_shift);
> +
> +		if (ONENAND_IS_2PLANE(this)) {
> +			/* Make the even block number */
> +			block &= ~1;
> +			/* Is it the odd plane? */
> +			if (addr & this->writesize)
> +				block++;
> +			page >>= 1;
> +		}
> +		page &= this->page_mask;
> +		break;
> +	}
> +
> +	if (block != -1) {
> +		/* Write 'DFS, FBA' of Flash */
> +		value = onenand_block_address(this, block);
> +		this->write_word(value, this->base +
> +				ONENAND_REG_START_ADDRESS1);
> +	}
> +
> +	if (page != -1) {
> +		/* Now we use page size operation */
> +		int sectors = 4, count = 4;
> +		int dataram;
> +
> +		switch (cmd) {
> +		default:
> +			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
> +				cmd = ONENAND_CMD_2X_PROG;
> +			dataram = ONENAND_CURRENT_BUFFERRAM(this);
> +			break;
> +		}
> +
> +		/* Write 'FPA, FSA' of Flash */
> +		value = onenand_page_address(page, sectors);
> +		this->write_word(value, this->base +
> +				ONENAND_REG_START_ADDRESS8);
> +
> +		/* Write 'BSA, BSC' of DataRAM */
> +		value = onenand_buffer_address(dataram, sectors, count);
> +		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
> +	}
> +
> +	/* Interrupt clear */
> +	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
> +
> +	/* Write command */
> +	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
> +
> +	return 0;
> +}
> +
> +/**
>   * onenand_command - [DEFAULT] Send command to OneNAND device
>   * @param mtd		MTD device structure
>   * @param cmd		the command to be sent
> @@ -1969,6 +2058,133 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
> 
> 
>  /**
> + * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
> + * @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
> + *
> + * OneNAND write out-of-band only for OTP
> + */
> +static int onenand_otp_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;
> +	int block, value, status;
> +
> +	to += ops->ooboffs;
> +
> +	/* Initialize retlen, in case of early exit */
> +	ops->oobretlen = 0;
> +
> +	oobsize = mtd->oobsize;
> +
> +	column = to & (mtd->oobsize - 1);
> +
> +	oobbuf = this->oob_buf;
> +
> +	/* Loop until all data write */
> +	while (written < len) {
> +		int thislen = min_t(int, oobsize, len - written);
> +
> +		cond_resched();
> +
> +		block = (int) (to >> this->erase_shift);
> +		/*
> +		 * Write 'DFS, FBA' of Flash
> +		 * Add: F100h DQ=DFS, FBA
> +		 */
> +
> +		value = onenand_block_address(this, block);
> +		this->write_word(value, this->base +
> +				ONENAND_REG_START_ADDRESS1);
> +
> +		/*
> +		 * Select DataRAM for DDP
> +		 * Add: F101h DQ=DBS
> +		 */
> +
> +		value = onenand_bufferram_address(this, block);
> +		this->write_word(value, this->base +
> +				ONENAND_REG_START_ADDRESS2);
> +		ONENAND_SET_NEXT_BUFFERRAM(this);
> +
> +		/*
> +		 * Enter OTP access mode
> +		 */
> +		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
> +		this->wait(mtd, FL_OTPING);
> +
> +		/* We send data to spare ram with oobsize
> +		 * to prevent byte access */
> +		memcpy(oobbuf + column, buf, thislen);
> +
> +		/*
> +		 * Write Data into DataRAM
> +		 * Add: 8th Word
> +		 * in sector0/spare/page0
> +		 * DQ=XXFCh
> +		 */
> +		this->write_bufferram(mtd, ONENAND_SPARERAM,
> +					oobbuf, 0, mtd->oobsize);
> +
> +		onenand_otp_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 "%s: write failed %d\n", __func__, ret);
> +			break;
> +		}
> +
> +		/* Exit OTP access mode */
> +		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
> +		this->wait(mtd, FL_RESETING);
> +
> +		status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
> +		status &= 0x60;
> +
> +		if (status == 0x60) {
> +			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
> +			printk(KERN_DEBUG "1st Block\tLOCKED\n");
> +			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
> +		} else if (status == 0x20) {
> +			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
> +			printk(KERN_DEBUG "1st Block\tLOCKED\n");
> +			printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
> +		} else if (status == 0x40) {
> +			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
> +			printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
> +			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
> +		} else {
> +			printk(KERN_DEBUG "Reboot to check\n");
> +		}
> +
> +		written += thislen;
> +		if (written == len)
> +			break;
> +
> +		to += mtd->writesize;
> +		buf += thislen;
> +		column = 0;
> +	}
> +
> +	ops->oobretlen = written;
> +
> +	return ret;
> +}
> +
> +/**
>   * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
>   * @param mtd		MTD device structure
>   * @param to		offset to write to
> @@ -2693,11 +2909,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
>  	struct mtd_oob_ops ops;
>  	int ret;
> 
> -	/* Enter OTP access mode */
> -	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
> -	this->wait(mtd, FL_OTPING);
> -
>  	if (FLEXONENAND(this)) {
> +
> +		/* Enter OTP access mode */
> +		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
> +		this->wait(mtd, FL_OTPING);
>  		/*
>  		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
>  		 * main area of page 49.
> @@ -2708,19 +2924,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
>  		ops.oobbuf = NULL;
>  		ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
>  		*retlen = ops.retlen;
> +
> +		/* Exit OTP access mode */
> +		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
> +		this->wait(mtd, FL_RESETING);
>  	} else {
>  		ops.mode = MTD_OOB_PLACE;
>  		ops.ooblen = len;
>  		ops.oobbuf = buf;
>  		ops.ooboffs = 0;
> -		ret = onenand_write_oob_nolock(mtd, from, &ops);
> +		ret = onenand_otp_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;
>  }
> 
> @@ -2751,16 +2967,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
>  	if (density < ONENAND_DEVICE_DENSITY_512Mb)
>  		otp_pages = 20;
>  	else
> -		otp_pages = 10;
> +		otp_pages = 50;
> 
>  	if (mode == MTD_OTP_FACTORY) {
>  		from += mtd->writesize * otp_pages;
> -		otp_pages = 64 - otp_pages;
> +		otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
>  	}
> 
>  	/* Check User/Factory boundary */
> -	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
> -		return 0;
> +	if (mode == MTD_OTP_USER) {
> +		if (((mtd->writesize * otp_pages) - (from + len)) < 0)
> +			return 0;
> +	} else {
> +		if (((mtd->writesize * otp_pages) - len) < 0)
> +			return 0;
> +	}
> 
>  	onenand_get_device(mtd, FL_OTPING);
>  	while (len > 0 && otp_pages > 0) {
> @@ -2783,13 +3004,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
>  			*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;
> +			buf += tmp_retlen;
> +			len -= tmp_retlen;
> +			*retlen += tmp_retlen;
> 
>  			if (ret)
>  				break;
> @@ -2902,21 +3122,11 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
>  	u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
>  	size_t retlen;
>  	int ret;
> +	unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;
> 
>  	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
>  						 : mtd->oobsize);
>  	/*
> -	 * Note: OTP lock operation
> -	 *       OTP block : 0xXXFC
> -	 *       1st block : 0xXXF3 (If chip support)
> -	 *       Both      : 0xXXF0 (If chip support)
> -	 */
> -	if (FLEXONENAND(this))
> -		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
> -	else
> -		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.
>  	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
> @@ -2926,6 +3136,25 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
>  	from = 0;
>  	len = FLEXONENAND(this) ? mtd->writesize : 16;
> 
> +	/*
> +	 * Note: OTP lock operation
> +	 *       OTP block : 0xXXFC			XX 1111 1100
> +	 *       1st block : 0xXXF3 (If chip support)	XX 1111 0011
> +	 *       Both      : 0xXXF0 (If chip support)	XX 1111 0000
> +	 */
> +	if (FLEXONENAND(this))
> +		otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
> +
> +	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
> +	if (otp == 1)
> +		buf[otp_lock_offset] = 0xFC;
> +	else if (otp == 2)
> +		buf[otp_lock_offset] = 0xF3;
> +	else if (otp == 3)
> +		buf[otp_lock_offset] = 0xF0;
> +	else if (otp != 0)
> +		printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
> +
>  	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
> 
>  	return ret ? : retlen;
> diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
> index 4e49f33..3c00b55 100644
> --- a/include/linux/mtd/onenand.h
> +++ b/include/linux/mtd/onenand.h
> @@ -152,6 +152,8 @@ struct onenand_chip {
>  /*
>   * Helper macros
>   */
> +#define ONENAND_PAGES_PER_BLOCK        (1<<6)
> +
>  #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
>  #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
>  #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)
> 
> 
-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-10-20  8:56 ` Artem Bityutskiy
@ 2009-10-20 14:13   ` Adrian Hunter
  2009-10-21 11:20     ` Amul Kumar Saha
  2009-10-21 11:30     ` Amul Kumar Saha
  0 siblings, 2 replies; 27+ messages in thread
From: Adrian Hunter @ 2009-10-20 14:13 UTC (permalink / raw)
  To: dedekind1@gmail.com
  Cc: Amul Kumar Saha, Kyungmin Park, David Woodhouse,
	linux-mtd@lists.infradead.org

Artem Bityutskiy wrote:
> I'm still waiting for Adrian to review this. Yesterday he said he will
> do this soon. In any case, I remember about this patch, just FYI.

Seems fine except why aren't
	onenand_otp_command()
	onenand_otp_write_oob_nolock()
inside an
	#ifdef CONFIG_MTD_ONENAND_OTP
	...
	#endif

> On Mon, 2009-10-12 at 11:31 +0530, Amul Kumar Saha wrote:
>> What is OTP in OneNAND?
>> The device includes,
>> 1. one block-sized OTP (One Time Programmable) area and
>> 2. user-controlled 1st block OTP(Block 0)
>> that can be used to increase system security or to provide identification capabilities.
>>
>> What is done?
>> In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0)
>> can be used as OTP area.
>> This area, available to the user, can be configured and locked with secured user information.
>> The OTP block can be read, programmed and locked using the same operations as any other NAND Flash
>> Array
>> memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed
>> to be a valid block.
>>
>> Why it is done? (Impact)
>> Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental
>> re-programming of data stored in the 1st block and OTP Block.
>>
>> Which problem it solves?
>> OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not
>> working with OneNAND devices.
>> Have observed the following in current OTP OneNAND Implmentation,
>> 1. DataSheet specific sequence to lock the OTP Area is not followed.
>> 2. Certain functions are quiet generic to cope with OTP specific activity.
>> This patch re-implements OTP support for OneNAND device.
>>
>> How it is done?
>> For all blocks, 8th word is available to the user.
>> However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
>> Therefore, in case of OTP Block, user usage on this area is prohibited.
>> Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the
>> process of issuing an OTP-Lock.
>> The possible conditions are:-
>> 1. Only 1st Block Lock
>> 2. Only OTP Block Lock
>> 3. Lock both the 1st Block and the OTP Block
>>
>> What Other feature additions have been done in this patch?
>> This patch adds feature for:-
>> 1. Only 1st Block Lock
>> 2. Lock both the 1st Block and the OTP Blocks
>>
>> Re-implemented OTP support for OneNAND
>> Added following features to OneNAND
>>       1. Lock only 1st Block in OneNAND
>>       2. Lock BOTH 1st Block and OTP Block in OneNAND
>>
>> Signed-off-by: Amul Kumar Saha <amul.saha at samsung.com>
>> ---
>>  drivers/mtd/onenand/onenand_base.c |  287 +++++++++++++++++++++++++++++++++----
>>  include/linux/mtd/onenand.h        |    2
>>  2 files changed, 260 insertions(+), 29 deletions(-)
>>
>> diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
>> index cb405bc..4d28268 100644
>> --- a/drivers/mtd/onenand/onenand_base.c
>> +++ b/drivers/mtd/onenand/onenand_base.c
>> @@ -11,7 +11,9 @@
>>   *
>>   *   Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
>>   *   Flex-OneNAND support
>> - *   Copyright (C) Samsung Electronics, 2008
>> + *   Amul Kumar Saha <amul.saha at samsung.com>
>> + *   OTP support
>> + *   Copyright (C) Samsung Electronics, 2009
>>   *
>>   * This program is free software; you can redistribute it and/or modify
>>   * it under the terms of the GNU General Public License version 2 as
>> @@ -43,6 +45,18 @@ MODULE_PARM_DESC(flex_bdry,        "SLC Boundary information for Flex-OneNAND"
>>                               "    : 0->Set boundary in unlocked status"
>>                               "    : 1->Set boundary in locked status");
>>
>> +/* Default OneNAND/Flex-OneNAND OTP options*/
>> +static int otp;
>> +
>> +module_param(otp, int, 0400);
>> +MODULE_PARM_DESC(otp,        "Corresponding behaviour of OneNAND in OTP"
>> +                     "Syntax : otp=LOCK_TYPE"
>> +                     "LOCK_TYPE : Keys issued, for specific OTP Lock type"
>> +                     "          : 0 -> Default (No Blocks Locked)"
>> +                     "          : 1 -> OTP Block lock"
>> +                     "          : 2 -> 1st Block lock"
>> +                     "          : 3 -> BOTH OTP Block and 1st Block lock");
>> +
>>  /**
>>   *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
>>   *  For now, we expose only 64 out of 80 ecc bytes
>> @@ -308,6 +322,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr)
>>  EXPORT_SYMBOL(flexonenand_region);
>>
>>  /**
>> + * onenand_otp_command - Send OTP specific command to OneNAND device
>> + * @param mtd         MTD device structure
>> + * @param cmd         the command to be sent
>> + * @param addr        offset to read from or write to
>> + * @param len         number of bytes to read or write
>> + */
>> +static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
>> +                             size_t len)
>> +{
>> +     struct onenand_chip *this = mtd->priv;
>> +     int value, block, page;
>> +
>> +     /* Address translation */
>> +     switch (cmd) {
>> +     case ONENAND_CMD_OTP_ACCESS:
>> +             block = (int) (addr >> this->erase_shift);
>> +             page = -1;
>> +             break;
>> +
>> +     default:
>> +             block = (int) (addr >> this->erase_shift);
>> +             page = (int) (addr >> this->page_shift);
>> +
>> +             if (ONENAND_IS_2PLANE(this)) {
>> +                     /* Make the even block number */
>> +                     block &= ~1;
>> +                     /* Is it the odd plane? */
>> +                     if (addr & this->writesize)
>> +                             block++;
>> +                     page >>= 1;
>> +             }
>> +             page &= this->page_mask;
>> +             break;
>> +     }
>> +
>> +     if (block != -1) {
>> +             /* Write 'DFS, FBA' of Flash */
>> +             value = onenand_block_address(this, block);
>> +             this->write_word(value, this->base +
>> +                             ONENAND_REG_START_ADDRESS1);
>> +     }
>> +
>> +     if (page != -1) {
>> +             /* Now we use page size operation */
>> +             int sectors = 4, count = 4;
>> +             int dataram;
>> +
>> +             switch (cmd) {
>> +             default:
>> +                     if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
>> +                             cmd = ONENAND_CMD_2X_PROG;
>> +                     dataram = ONENAND_CURRENT_BUFFERRAM(this);
>> +                     break;
>> +             }
>> +
>> +             /* Write 'FPA, FSA' of Flash */
>> +             value = onenand_page_address(page, sectors);
>> +             this->write_word(value, this->base +
>> +                             ONENAND_REG_START_ADDRESS8);
>> +
>> +             /* Write 'BSA, BSC' of DataRAM */
>> +             value = onenand_buffer_address(dataram, sectors, count);
>> +             this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
>> +     }
>> +
>> +     /* Interrupt clear */
>> +     this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
>> +
>> +     /* Write command */
>> +     this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
>> +
>> +     return 0;
>> +}
>> +
>> +/**
>>   * onenand_command - [DEFAULT] Send command to OneNAND device
>>   * @param mtd                MTD device structure
>>   * @param cmd                the command to be sent
>> @@ -1969,6 +2058,133 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
>>
>>
>>  /**
>> + * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
>> + * @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
>> + *
>> + * OneNAND write out-of-band only for OTP
>> + */
>> +static int onenand_otp_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;
>> +     int block, value, status;
>> +
>> +     to += ops->ooboffs;
>> +
>> +     /* Initialize retlen, in case of early exit */
>> +     ops->oobretlen = 0;
>> +
>> +     oobsize = mtd->oobsize;
>> +
>> +     column = to & (mtd->oobsize - 1);
>> +
>> +     oobbuf = this->oob_buf;
>> +
>> +     /* Loop until all data write */
>> +     while (written < len) {
>> +             int thislen = min_t(int, oobsize, len - written);
>> +
>> +             cond_resched();
>> +
>> +             block = (int) (to >> this->erase_shift);
>> +             /*
>> +              * Write 'DFS, FBA' of Flash
>> +              * Add: F100h DQ=DFS, FBA
>> +              */
>> +
>> +             value = onenand_block_address(this, block);
>> +             this->write_word(value, this->base +
>> +                             ONENAND_REG_START_ADDRESS1);
>> +
>> +             /*
>> +              * Select DataRAM for DDP
>> +              * Add: F101h DQ=DBS
>> +              */
>> +
>> +             value = onenand_bufferram_address(this, block);
>> +             this->write_word(value, this->base +
>> +                             ONENAND_REG_START_ADDRESS2);
>> +             ONENAND_SET_NEXT_BUFFERRAM(this);
>> +
>> +             /*
>> +              * Enter OTP access mode
>> +              */
>> +             this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
>> +             this->wait(mtd, FL_OTPING);
>> +
>> +             /* We send data to spare ram with oobsize
>> +              * to prevent byte access */
>> +             memcpy(oobbuf + column, buf, thislen);
>> +
>> +             /*
>> +              * Write Data into DataRAM
>> +              * Add: 8th Word
>> +              * in sector0/spare/page0
>> +              * DQ=XXFCh
>> +              */
>> +             this->write_bufferram(mtd, ONENAND_SPARERAM,
>> +                                     oobbuf, 0, mtd->oobsize);
>> +
>> +             onenand_otp_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 "%s: write failed %d\n", __func__, ret);
>> +                     break;
>> +             }
>> +
>> +             /* Exit OTP access mode */
>> +             this->command(mtd, ONENAND_CMD_RESET, 0, 0);
>> +             this->wait(mtd, FL_RESETING);
>> +
>> +             status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
>> +             status &= 0x60;
>> +
>> +             if (status == 0x60) {
>> +                     printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
>> +                     printk(KERN_DEBUG "1st Block\tLOCKED\n");
>> +                     printk(KERN_DEBUG "OTP Block\tLOCKED\n");
>> +             } else if (status == 0x20) {
>> +                     printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
>> +                     printk(KERN_DEBUG "1st Block\tLOCKED\n");
>> +                     printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
>> +             } else if (status == 0x40) {
>> +                     printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
>> +                     printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
>> +                     printk(KERN_DEBUG "OTP Block\tLOCKED\n");
>> +             } else {
>> +                     printk(KERN_DEBUG "Reboot to check\n");
>> +             }
>> +
>> +             written += thislen;
>> +             if (written == len)
>> +                     break;
>> +
>> +             to += mtd->writesize;
>> +             buf += thislen;
>> +             column = 0;
>> +     }
>> +
>> +     ops->oobretlen = written;
>> +
>> +     return ret;
>> +}
>> +
>> +/**
>>   * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
>>   * @param mtd                MTD device structure
>>   * @param to         offset to write to
>> @@ -2693,11 +2909,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
>>       struct mtd_oob_ops ops;
>>       int ret;
>>
>> -     /* Enter OTP access mode */
>> -     this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
>> -     this->wait(mtd, FL_OTPING);
>> -
>>       if (FLEXONENAND(this)) {
>> +
>> +             /* Enter OTP access mode */
>> +             this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
>> +             this->wait(mtd, FL_OTPING);
>>               /*
>>                * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
>>                * main area of page 49.
>> @@ -2708,19 +2924,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
>>               ops.oobbuf = NULL;
>>               ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
>>               *retlen = ops.retlen;
>> +
>> +             /* Exit OTP access mode */
>> +             this->command(mtd, ONENAND_CMD_RESET, 0, 0);
>> +             this->wait(mtd, FL_RESETING);
>>       } else {
>>               ops.mode = MTD_OOB_PLACE;
>>               ops.ooblen = len;
>>               ops.oobbuf = buf;
>>               ops.ooboffs = 0;
>> -             ret = onenand_write_oob_nolock(mtd, from, &ops);
>> +             ret = onenand_otp_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;
>>  }
>>
>> @@ -2751,16 +2967,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
>>       if (density < ONENAND_DEVICE_DENSITY_512Mb)
>>               otp_pages = 20;
>>       else
>> -             otp_pages = 10;
>> +             otp_pages = 50;
>>
>>       if (mode == MTD_OTP_FACTORY) {
>>               from += mtd->writesize * otp_pages;
>> -             otp_pages = 64 - otp_pages;
>> +             otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
>>       }
>>
>>       /* Check User/Factory boundary */
>> -     if (((mtd->writesize * otp_pages) - (from + len)) < 0)
>> -             return 0;
>> +     if (mode == MTD_OTP_USER) {
>> +             if (((mtd->writesize * otp_pages) - (from + len)) < 0)
>> +                     return 0;
>> +     } else {
>> +             if (((mtd->writesize * otp_pages) - len) < 0)
>> +                     return 0;
>> +     }
>>
>>       onenand_get_device(mtd, FL_OTPING);
>>       while (len > 0 && otp_pages > 0) {
>> @@ -2783,13 +3004,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
>>                       *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;
>> +                     buf += tmp_retlen;
>> +                     len -= tmp_retlen;
>> +                     *retlen += tmp_retlen;
>>
>>                       if (ret)
>>                               break;
>> @@ -2902,21 +3122,11 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
>>       u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
>>       size_t retlen;
>>       int ret;
>> +     unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;
>>
>>       memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
>>                                                : mtd->oobsize);
>>       /*
>> -      * Note: OTP lock operation
>> -      *       OTP block : 0xXXFC
>> -      *       1st block : 0xXXF3 (If chip support)
>> -      *       Both      : 0xXXF0 (If chip support)
>> -      */
>> -     if (FLEXONENAND(this))
>> -             buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
>> -     else
>> -             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.
>>        * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
>> @@ -2926,6 +3136,25 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
>>       from = 0;
>>       len = FLEXONENAND(this) ? mtd->writesize : 16;
>>
>> +     /*
>> +      * Note: OTP lock operation
>> +      *       OTP block : 0xXXFC                     XX 1111 1100
>> +      *       1st block : 0xXXF3 (If chip support)   XX 1111 0011
>> +      *       Both      : 0xXXF0 (If chip support)   XX 1111 0000
>> +      */
>> +     if (FLEXONENAND(this))
>> +             otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
>> +
>> +     /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
>> +     if (otp == 1)
>> +             buf[otp_lock_offset] = 0xFC;
>> +     else if (otp == 2)
>> +             buf[otp_lock_offset] = 0xF3;
>> +     else if (otp == 3)
>> +             buf[otp_lock_offset] = 0xF0;
>> +     else if (otp != 0)
>> +             printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
>> +
>>       ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
>>
>>       return ret ? : retlen;
>> diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
>> index 4e49f33..3c00b55 100644
>> --- a/include/linux/mtd/onenand.h
>> +++ b/include/linux/mtd/onenand.h
>> @@ -152,6 +152,8 @@ struct onenand_chip {
>>  /*
>>   * Helper macros
>>   */
>> +#define ONENAND_PAGES_PER_BLOCK        (1<<6)
>> +
>>  #define ONENAND_CURRENT_BUFFERRAM(this)              (this->bufferram_index)
>>  #define ONENAND_NEXT_BUFFERRAM(this)         (this->bufferram_index ^ 1)
>>  #define ONENAND_SET_NEXT_BUFFERRAM(this)     (this->bufferram_index ^= 1)
>>
>>
> --
> Best Regards,
> Artem Bityutskiy (Артём Битюцкий)
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-10-20 14:13   ` Adrian Hunter
@ 2009-10-21 11:20     ` Amul Kumar Saha
  2009-10-23  6:11       ` Artem Bityutskiy
  2009-10-21 11:30     ` Amul Kumar Saha
  1 sibling, 1 reply; 27+ messages in thread
From: Amul Kumar Saha @ 2009-10-21 11:20 UTC (permalink / raw)
  To: Adrian Hunter, dedekind1; +Cc: David Woodhouse, Kyungmin Park, linux-mtd

Hi Adrian,


> Artem Bityutskiy wrote:
>> I'm still waiting for Adrian to review this. Yesterday he said he will
>> do this soon. In any case, I remember about this patch, just FYI.
>
> Seems fine except why aren't
> onenand_otp_command()
> onenand_otp_write_oob_nolock()
> inside an
> #ifdef CONFIG_MTD_ONENAND_OTP
> ...
> #endif

Accepted and Corrected.

Regards,
Amul Kumar Saha 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-10-20 14:13   ` Adrian Hunter
  2009-10-21 11:20     ` Amul Kumar Saha
@ 2009-10-21 11:30     ` Amul Kumar Saha
  1 sibling, 0 replies; 27+ messages in thread
From: Amul Kumar Saha @ 2009-10-21 11:30 UTC (permalink / raw)
  To: Adrian Hunter, dedekind1; +Cc: David Woodhouse, Kyungmin Park, linux-mtd

What is OTP in OneNAND?
The device includes,
1. one block-sized OTP (One Time Programmable) area and
2. user-controlled 1st block OTP(Block 0)
that can be used to increase system security or to provide identification capabilities.

What is done?
In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0)
can be used as OTP area.
This area, available to the user, can be configured and locked with secured user information.
The OTP block can be read, programmed and locked using the same operations as any other NAND Flash
Array
memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed
to be a valid block.

Why it is done? (Impact)
Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental
re-programming of data stored in the 1st block and OTP Block.

Which problem it solves?
OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not
working with OneNAND devices.
Have observed the following in current OTP OneNAND Implmentation,
1. DataSheet specific sequence to lock the OTP Area is not followed.
2. Certain functions are quiet generic to cope with OTP specific activity.
This patch re-implements OTP support for OneNAND device.

How it is done?
For all blocks, 8th word is available to the user.
However,in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area.
Therefore, in case of OTP Block, user usage on this area is prohibited.
Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the
process of issuing an OTP-Lock.
The possible conditions are:-
1. Only 1st Block Lock
2. Only OTP Block Lock
3. Lock both the 1st Block and the OTP Block

What Other feature additions have been done in this patch?
This patch adds feature for:-
1. Only 1st Block Lock
2. Lock both the 1st Block and the OTP Blocks

Re-implemented OTP support for OneNAND
Added following features to OneNAND
	1. Lock only 1st Block in OneNAND
	2. Lock BOTH 1st Block and OTP Block in OneNAND

Signed-off-by: Amul Kumar Saha <amul.saha@samsung.com>
---
 drivers/mtd/onenand/onenand_base.c |  288 +++++++++++++++++++++++++++++++++----
 include/linux/mtd/onenand.h        |    2
 2 files changed, 261 insertions(+), 29 deletions(-)

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index ff66e43..f5f6c09 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -11,7 +11,9 @@
  *
  *	Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
  *	Flex-OneNAND support
- *	Copyright (C) Samsung Electronics, 2008
+ *	Amul Kumar Saha <amul.saha at samsung.com>
+ *	OTP support
+ *	Copyright (C) Samsung Electronics, 2009
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -43,6 +45,18 @@ MODULE_PARM_DESC(flex_bdry,	"SLC Boundary information for Flex-OneNAND"
 				"    : 0->Set boundary in unlocked status"
 				"    : 1->Set boundary in locked status");

+/* Default OneNAND/Flex-OneNAND OTP options*/
+static int otp;
+
+module_param(otp, int, 0400);
+MODULE_PARM_DESC(otp,	"Corresponding behaviour of OneNAND in OTP"
+			"Syntax : otp=LOCK_TYPE"
+			"LOCK_TYPE : Keys issued, for specific OTP Lock type"
+			"	   : 0 -> Default (No Blocks Locked)"
+			"	   : 1 -> OTP Block lock"
+			"	   : 2 -> 1st Block lock"
+			"	   : 3 -> BOTH OTP Block and 1st Block lock");
+
 /**
  *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
  *  For now, we expose only 64 out of 80 ecc bytes
@@ -2557,6 +2571,208 @@ static void onenand_unlock_all(struct mtd_info *mtd)

 #ifdef CONFIG_MTD_ONENAND_OTP

+/**
+ * onenand_otp_command - Send OTP specific command to OneNAND device
+ * @param mtd	 MTD device structure
+ * @param cmd	 the command to be sent
+ * @param addr	 offset to read from or write to
+ * @param len	 number of bytes to read or write
+ */
+static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
+				size_t len)
+{
+	struct onenand_chip *this = mtd->priv;
+	int value, block, page;
+
+	/* Address translation */
+	switch (cmd) {
+	case ONENAND_CMD_OTP_ACCESS:
+		block = (int) (addr >> this->erase_shift);
+		page = -1;
+		break;
+
+	default:
+		block = (int) (addr >> this->erase_shift);
+		page = (int) (addr >> this->page_shift);
+
+		if (ONENAND_IS_2PLANE(this)) {
+			/* Make the even block number */
+			block &= ~1;
+			/* Is it the odd plane? */
+			if (addr & this->writesize)
+				block++;
+			page >>= 1;
+		}
+		page &= this->page_mask;
+		break;
+	}
+
+	if (block != -1) {
+		/* Write 'DFS, FBA' of Flash */
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+	}
+
+	if (page != -1) {
+		/* Now we use page size operation */
+		int sectors = 4, count = 4;
+		int dataram;
+
+		switch (cmd) {
+		default:
+			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+				cmd = ONENAND_CMD_2X_PROG;
+			dataram = ONENAND_CURRENT_BUFFERRAM(this);
+			break;
+		}
+
+		/* Write 'FPA, FSA' of Flash */
+		value = onenand_page_address(page, sectors);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS8);
+
+		/* Write 'BSA, BSC' of DataRAM */
+		value = onenand_buffer_address(dataram, sectors, count);
+		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+	}
+
+	/* Interrupt clear */
+	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+	/* Write command */
+	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+	return 0;
+}
+
+/**
+ * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * @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
+ *
+ * OneNAND write out-of-band only for OTP
+ */
+static int onenand_otp_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;
+	int block, value, status;
+
+	to += ops->ooboffs;
+
+	/* Initialize retlen, in case of early exit */
+	ops->oobretlen = 0;
+
+	oobsize = mtd->oobsize;
+
+	column = to & (mtd->oobsize - 1);
+
+	oobbuf = this->oob_buf;
+
+	/* Loop until all data write */
+	while (written < len) {
+		int thislen = min_t(int, oobsize, len - written);
+
+		cond_resched();
+
+		block = (int) (to >> this->erase_shift);
+		/*
+		 * Write 'DFS, FBA' of Flash
+		 * Add: F100h DQ=DFS, FBA
+		 */
+
+		value = onenand_block_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS1);
+
+		/*
+		 * Select DataRAM for DDP
+		 * Add: F101h DQ=DBS
+		 */
+
+		value = onenand_bufferram_address(this, block);
+		this->write_word(value, this->base +
+				ONENAND_REG_START_ADDRESS2);
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+
+		/*
+		 * Enter OTP access mode
+		 */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
+
+		/* We send data to spare ram with oobsize
+		 * to prevent byte access */
+		memcpy(oobbuf + column, buf, thislen);
+
+		/*
+		 * Write Data into DataRAM
+		 * Add: 8th Word
+		 * in sector0/spare/page0
+		 * DQ=XXFCh
+		 */
+		this->write_bufferram(mtd, ONENAND_SPARERAM,
+					oobbuf, 0, mtd->oobsize);
+
+		onenand_otp_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 "%s: write failed %d\n", __func__, ret);
+			break;
+		}
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
+
+		status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+		status &= 0x60;
+
+		if (status == 0x60) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else if (status == 0x20) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tLOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
+		} else if (status == 0x40) {
+			printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+			printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
+			printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+		} else {
+			printk(KERN_DEBUG "Reboot to check\n");
+		}
+
+		written += thislen;
+		if (written == len)
+			break;
+
+		to += mtd->writesize;
+		buf += thislen;
+		column = 0;
+	}
+
+	ops->oobretlen = written;
+
+	return ret;
+}
+
 /* Internal OTP operation */
 typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
 		size_t *retlen, u_char *buf);
@@ -2659,11 +2875,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 	struct mtd_oob_ops ops;
 	int ret;

-	/* Enter OTP access mode */
-	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
-	this->wait(mtd, FL_OTPING);
-
 	if (FLEXONENAND(this)) {
+
+		/* Enter OTP access mode */
+		this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+		this->wait(mtd, FL_OTPING);
 		/*
 		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
 		 * main area of page 49.
@@ -2674,19 +2890,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
 		ops.oobbuf = NULL;
 		ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
 		*retlen = ops.retlen;
+
+		/* Exit OTP access mode */
+		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+		this->wait(mtd, FL_RESETING);
 	} else {
 		ops.mode = MTD_OOB_PLACE;
 		ops.ooblen = len;
 		ops.oobbuf = buf;
 		ops.ooboffs = 0;
-		ret = onenand_write_oob_nolock(mtd, from, &ops);
+		ret = onenand_otp_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;
 }

@@ -2717,16 +2933,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 	if (density < ONENAND_DEVICE_DENSITY_512Mb)
 		otp_pages = 20;
 	else
-		otp_pages = 10;
+		otp_pages = 50;

 	if (mode == MTD_OTP_FACTORY) {
 		from += mtd->writesize * otp_pages;
-		otp_pages = 64 - otp_pages;
+		otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
 	}

 	/* Check User/Factory boundary */
-	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
-		return 0;
+	if (mode == MTD_OTP_USER) {
+		if (((mtd->writesize * otp_pages) - (from + len)) < 0)
+			return 0;
+	} else {
+		if (((mtd->writesize * otp_pages) - len) < 0)
+			return 0;
+	}

 	onenand_get_device(mtd, FL_OTPING);
 	while (len > 0 && otp_pages > 0) {
@@ -2749,13 +2970,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 			*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;
+			buf += tmp_retlen;
+			len -= tmp_retlen;
+			*retlen += tmp_retlen;

 			if (ret)
 				break;
@@ -2868,21 +3088,11 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
 	size_t retlen;
 	int ret;
+	unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;

 	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
 						 : mtd->oobsize);
 	/*
-	 * Note: OTP lock operation
-	 *       OTP block : 0xXXFC
-	 *       1st block : 0xXXF3 (If chip support)
-	 *       Both      : 0xXXF0 (If chip support)
-	 */
-	if (FLEXONENAND(this))
-		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
-	else
-		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.
 	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
@@ -2892,10 +3102,30 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 	from = 0;
 	len = FLEXONENAND(this) ? mtd->writesize : 16;

+	/*
+	 * Note: OTP lock operation
+	 *       OTP block : 0xXXFC			XX 1111 1100
+	 *       1st block : 0xXXF3 (If chip support)	XX 1111 0011
+	 *       Both      : 0xXXF0 (If chip support)	XX 1111 0000
+	 */
+	if (FLEXONENAND(this))
+		otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
+
+	/* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+	if (otp == 1)
+		buf[otp_lock_offset] = 0xFC;
+	else if (otp == 2)
+		buf[otp_lock_offset] = 0xF3;
+	else if (otp == 3)
+		buf[otp_lock_offset] = 0xF0;
+	else if (otp != 0)
+		printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
+
 	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);

 	return ret ? : retlen;
 }
+
 #endif	/* CONFIG_MTD_ONENAND_OTP */

 /**
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 4e49f33..3c00b55 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -152,6 +152,8 @@ struct onenand_chip {
 /*
  * Helper macros
  */
+#define ONENAND_PAGES_PER_BLOCK        (1<<6)
+
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH] [OneNAND] OTP support re-implementation 1/1
  2009-10-21 11:20     ` Amul Kumar Saha
@ 2009-10-23  6:11       ` Artem Bityutskiy
  0 siblings, 0 replies; 27+ messages in thread
From: Artem Bityutskiy @ 2009-10-23  6:11 UTC (permalink / raw)
  To: Amul Kumar Saha; +Cc: David Woodhouse, Kyungmin Park, linux-mtd, Adrian Hunter

On Wed, 2009-10-21 at 16:50 +0530, Amul Kumar Saha wrote:
> Hi Adrian,
> 
> 
> > Artem Bityutskiy wrote:
> >> I'm still waiting for Adrian to review this. Yesterday he said he will
> >> do this soon. In any case, I remember about this patch, just FYI.
> >
> > Seems fine except why aren't
> > onenand_otp_command()
> > onenand_otp_write_oob_nolock()
> > inside an
> > #ifdef CONFIG_MTD_ONENAND_OTP
> > ...
> > #endif
> 
> Accepted and Corrected.

It is in my l2 tree now, thanks.

-- 
Best Regards,
Artem Bityutskiy (Артём Битюцкий)

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2009-10-23  6:11 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-08-17 10:43 [PATCH] [OneNAND] OTP support re-implementation 1/1 Amul Kumar Saha
2009-08-26  5:41 ` Amul Kumar Saha
2009-08-26  5:58   ` Artem Bityutskiy
2009-08-27  6:37     ` Amul Kumar Saha
2009-08-28 13:38       ` Artem Bityutskiy
2009-08-31  9:19         ` Amul Kumar Saha
2009-08-31  9:34           ` Kyungmin Park
2009-09-02  5:55             ` Amul Kumar Saha
2009-09-02  5:59             ` Amul Kumar Saha
2009-09-02  6:10               ` Artem Bityutskiy
2009-09-03  5:51                 ` Amul Kumar Saha
2009-09-03  6:10                   ` Artem Bityutskiy
2009-09-03 10:37                     ` Amul Kumar Saha
2009-09-03 11:19                       ` Artem Bityutskiy
2009-09-07  9:45                         ` Amul Kumar Saha
2009-09-07  9:57                           ` Kyungmin Park
2009-09-16  3:33                             ` Amul Kumar Saha
2009-09-16  6:40                               ` Artem Bityutskiy
2009-09-28  9:38                           ` Artem Bityutskiy
2009-10-01  6:48                             ` Amul Kumar Saha
2009-10-12  4:42                             ` Amul Kumar Saha
  -- strict thread matches above, loose matches on Subject: below --
2009-10-12  6:01 Amul Kumar Saha
2009-10-20  8:56 ` Artem Bityutskiy
2009-10-20 14:13   ` Adrian Hunter
2009-10-21 11:20     ` Amul Kumar Saha
2009-10-23  6:11       ` Artem Bityutskiy
2009-10-21 11:30     ` Amul Kumar Saha

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).