[RFC PATCH] OneNAND Sync. Burst Block Read support

Kyungmin Park kyungmin.park at samsung.com
Wed Jan 10 21:52:34 EST 2007


Hi,

Here's another OneNAND feature Sync. Burst Block Read (SBBR) support.

Recent OneNAND chip supports Sync. Burst Block Read.
Basically it is similar with the previous read-while-load except we don't issue next command.
We only send just one command. then OneNAND load next page to another buffer ram automatically.

But there's one dependency. It is only perform when you configure your DRAM & OneNAND with synchorous mode.
In the code it will be checked.

Note 0: We don't consider the chip boundary condition in SBBR since it has block boundary.
Note 1: Now we only use SBBR in all pages. but actually it supports several pages. I don't see any upper layer which uses serveral page requests in MTD


Any comments are welcome.

Thank you,
Kyungmin Park

--

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 7480776..5e22d7b 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -190,7 +190,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
 static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
 {
 	struct onenand_chip *this = mtd->priv;
-	int value, readcmd = 0, block_cmd = 0;
+	int value, block_cmd = 0;
 	int block, page;
 
 	/* Address translation */
@@ -245,7 +245,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 	if (page != -1) {
 		/* Now we use page size operation */
 		int sectors = 4, count = 4;
-		int dataram;
+		int dataram, readcmd = 0;
 
 		switch (cmd) {
 		case ONENAND_CMD_READ:
@@ -254,6 +254,20 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 			readcmd = 1;
 			break;
 
+		case ONENAND_CMD_SYNC_BLOCK_READ:
+		{
+			int npages;
+
+			npages = (len >> this->page_shift) & this->page_mask;
+			/* We always read from DataRAM 0 */
+			dataram = ONENAND_INIT_BUFFERRAM(this);
+			readcmd = 1;
+
+			/* Write 'FPC' of Flash */
+			this->write_word(npages, this->base + ONENAND_REG_START_ADDRESS5);
+			break;
+		}
+
 		default:
 			dataram = ONENAND_CURRENT_BUFFERRAM(this);
 			break;
@@ -591,8 +605,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
 	int i;
 
 	block = (int) (addr >> this->erase_shift);
-	page = (int) (addr >> this->page_shift);
-	page &= this->page_mask;
+	page = (int) (addr >> this->page_shift) & this->page_mask;
 
 	i = ONENAND_CURRENT_BUFFERRAM(this);
 
@@ -621,8 +634,7 @@ static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
 	int i;
 
 	block = (int) (addr >> this->erase_shift);
-	page = (int) (addr >> this->page_shift);
-	page &= this->page_mask;
+	page = (int) (addr >> this->page_shift) & this->page_mask;
 
 	/* Invalidate BufferRAM */
 	for (i = 0; i < MAX_BUFFERRAM; i++) {
@@ -694,6 +706,68 @@ static void onenand_release_device(struct mtd_info *mtd)
 }
 
 /**
+ * onenand_read - [MTD Interface] Sync. Burst Block Read data from flash
+ * @param mtd		MTD device structure
+ * @param from		offset to read from
+ * @param len		number of bytes to read
+ * @param retlen	pointer to variable to store the number of read bytes
+ * @param buf		the databuffer to put data
+ *
+ * Sync. Burst Block Read with ecc
+*/
+static int onenand_sync_block_read(struct mtd_info *mtd, loff_t from,
+	size_t len, size_t *retlen, u_char *buf)
+{
+	struct onenand_chip *this = mtd->priv;
+	struct mtd_ecc_stats stats;
+	int thislen = mtd->writesize;
+	int read = 0, ret = 0;
+
+	/* Grab the lock and see if the device is available */
+	onenand_get_device(mtd, FL_READING);
+
+	this->command(mtd, ONENAND_CMD_SYNC_BLOCK_READ, from, len);
+
+	stats = mtd->ecc_stats;
+	while (read < len) {
+		ret = this->wait(mtd, FL_READING);
+		onenand_update_bufferram(mtd, from, !ret);
+		if (ret)
+			break;
+
+		/* Read from DataRAM */
+		this->read_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen);
+		read += thislen;
+		if (read == len)
+			break;
+
+		from += thislen;
+		buf += thislen;
+
+		/* Set up for next read from bufferRAM */
+		ONENAND_SET_NEXT_BUFFERRAM(this);
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	onenand_release_device(mtd);
+
+	/*
+	 * Return success, if no ECC failures, else -EBADMSG
+	 * fs driver will take care of that, because
+	 * retlen == desired len and result == -EBADMSG
+	 */
+	*retlen = read;
+
+	if (mtd->ecc_stats.failed - stats.failed)
+		return -EBADMSG;
+
+	if (ret)
+		return ret;
+
+	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
+/**
  * onenand_read - [MTD Interface] Read data from flash
  * @param mtd		MTD device structure
  * @param from		offset to read from
@@ -721,6 +795,10 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
 		return -EINVAL;
 	}
 
+	/* Sync. Burst Block Read support */
+	if (len == mtd->erasesize && SYNC_BLOCK_READ_SUPPORT(this))
+		return onenand_sync_block_read(mtd, from, len, retlen, buf);
+
 	/* Grab the lock and see if the device is available */
 	onenand_get_device(mtd, FL_READING);
 
@@ -744,6 +822,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
 		thislen = mtd->writesize - column;
 	while (!ret) {
 		/* If there is more to load then start next load */
+		cond_resched();
 		from += thislen;
 		if (read + thislen < len) {
 			this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize);
@@ -1481,28 +1560,27 @@ static int onenand_unlock_all(struct mtd_info *mtd)
 	struct onenand_chip *this = mtd->priv;
 
 	if (this->options & ONENAND_HAS_UNLOCK_ALL) {
+		size_t len = mtd->erasesize;
+		loff_t ofs = 0;
+
 		/* Write unlock command */
 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
 
-		/* There's no return value */
-		this->wait(mtd, FL_LOCKING);
-
 		/* Sanity check */
 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
 		    & ONENAND_CTRL_ONGO)
 			continue;
 
 		/* Workaround for all block unlock in DDP */
-		if (this->device_id & ONENAND_DEVICE_IS_DDP) {
-			loff_t ofs;
-			size_t len;
-
+		if (this->device_id & ONENAND_DEVICE_IS_DDP)
 			/* 1st block on another chip */
 			ofs = this->chipsize >> 1;
-			len = 1 << this->erase_shift;
 
-			onenand_unlock(mtd, ofs, len);
-		}
+		/* To update lock status */
+		onenand_unlock(mtd, ofs, len);
+
+		/* There's no return value */
+		this->wait(mtd, FL_LOCKING);
 
 		onenand_check_lock_status(this);
 
@@ -1813,12 +1891,14 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 #endif	/* CONFIG_MTD_ONENAND_OTP */
 
 /**
- * onenand_lock_scheme - Check and set OneNAND lock scheme
+ * onenand_check_features - Check and set OneNAND features
  * @param mtd		MTD data structure
  *
- * Check and set OneNAND lock scheme
+ * Check and set OneNAND featurs
+ * - lock scheme
+ * - Sync. burst block read
  */
-static void onenand_lock_scheme(struct mtd_info *mtd)
+static void onenand_check_features(struct mtd_info *mtd)
 {
 	struct onenand_chip *this = mtd->priv;
 	unsigned int density, process;
@@ -1841,6 +1921,18 @@ static void onenand_lock_scheme(struct mtd_info *mtd)
 			this->options |= ONENAND_HAS_CONT_LOCK;
 		}
 	}
+
+	/* Check Sync. Burst block read feature */
+	if (density >= ONENAND_DEVICE_DENSITY_1Gb && process) {
+		int syscfg;
+
+		/* It has dependency with Sync. Burst Read Mode */
+		syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+		if (syscfg & ONENAND_SYS_CFG1_SYNC_READ) {
+			printk(KERN_DEBUG "Sync. Burst Block Read support\n");
+			this->options |= ONENAND_HAS_SYNC_BLOCK_READ;
+		}
+	}
 }
 
 /**
@@ -1969,8 +2061,8 @@ static int onenand_probe(struct mtd_info *mtd)
 
 	mtd->size = this->chipsize;
 
-	/* Check OneNAND lock scheme */
-	onenand_lock_scheme(mtd);
+	/* Check OneNAND features */
+	onenand_check_features(mtd);
 
 	return 0;
 }
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index f775a7a..e4c3fb8 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -140,6 +140,7 @@ struct onenand_chip {
 /*
  * Helper macros
  */
+#define ONENAND_INIT_BUFFERRAM(this)		(this->bufferram_index = 0)
 #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)
@@ -150,6 +151,8 @@ struct onenand_chip {
 #define ONENAND_SET_SYS_CFG1(v, this)					\
 	(this->write_word(v, this->base + ONENAND_REG_SYS_CFG1))
 
+#define SYNC_BLOCK_READ_SUPPORT(this)		(this->options & ONENAND_HAS_SYNC_BLOCK_READ)
+
 /* Check byte access in OneNAND */
 #define ONENAND_CHECK_BYTE_ACCESS(addr)		(addr & 0x1)
 
@@ -158,6 +161,7 @@ struct onenand_chip {
  */
 #define ONENAND_HAS_CONT_LOCK		(0x0001)
 #define ONENAND_HAS_UNLOCK_ALL		(0x0002)
+#define ONENAND_HAS_SYNC_BLOCK_READ	(0x0004)
 #define ONENAND_PAGEBUF_ALLOC		(0x1000)
 
 /*
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
index e31c8f5..d3b19a1 100644
--- a/include/linux/mtd/onenand_regs.h
+++ b/include/linux/mtd/onenand_regs.h
@@ -112,6 +112,7 @@
 #define ONENAND_CMD_LOCK		(0x2A)
 #define ONENAND_CMD_LOCK_TIGHT		(0x2C)
 #define ONENAND_CMD_UNLOCK_ALL		(0x27)
+#define ONENAND_CMD_SYNC_BLOCK_READ	(0x0A)
 #define ONENAND_CMD_ERASE		(0x94)
 #define ONENAND_CMD_RESET		(0xF0)
 #define ONENAND_CMD_OTP_ACCESS		(0x65)


More information about the linux-mtd mailing list