Skip to contentSkip to conversation

mtd/nand : Add function board_nand_init_tail() for some special NAND controllers

PATCH New Shengzhou Liu 3 participants 7 messages Nov 22, 2011 – Nov 23, 2011

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/3 mtd/nand : Add function board_nand_init_tail() for some special NAND controllers

In some NAND controllers there is a size limitation of RAM buffer(2K bytes). To support large-page NAND chips with greater than 2K pagesize, we need a large buffer, but we don't know pagesize before calling nand_scan_ident(), for more flexible and to identify different cases of large-page greater than 2K bytes, we have a board_nand_init_tail() between nand_scan_ident() and nand_scan_tail().

---
based on 'master' branch of Wolfgang's tree.

 drivers/mtd/nand/nand.c |   14 +++++++++++++-
 1 files changed, 13 insertions(+), 1 deletions(-)
drivers/mtd/nand/nand.c +13 -1
index d987f4c..2bafe47 100644
@@ -39,6 +39,13 @@ static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIS
 static const char default_nand_name[] = "nand";
 static __attribute__((unused)) char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8];
 
+int __board_nand_init_tail(struct mtd_info *mtd, struct nand_chip *nand)
+{
+	/* Allow for init at tail in controller-specific file for some reason */
+}
+void board_nand_init_tail(struct mtd_info *mtd, struct nand_chip *nand)
+__attribute__((weak, alias("__board_nand_init_tail")));
+
 static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
 			   ulong base_addr)
 {
@@ -51,7 +58,12 @@ static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
 
 	nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;
 	if (board_nand_init(nand) == 0) {
-		if (nand_scan(mtd, maxchips) == 0) {
+		if (!nand_scan_ident(mtd, maxchips, NULL)) {
+			board_nand_init_tail(mtd, nand);
+			if (nand_scan_tail(mtd)) {
+				mtd->name = NULL;
+				return;
+			}
 			if (!mtd->name)
 				mtd->name = (char *)default_nand_name;
 #ifdef CONFIG_NEEDS_MANUAL_RELOC
Signature · 1 line
1.6.4
#1 lore raw

PATCH 2/3 mtd/nand : workaround for Freescale FCM to support 4k pagesize Nand chip

Freescale FCM controller has a 2K size limitation of buffer RAM. In order to support the Nand flash chip whose page size is larger than 2K bytes, we read/write 2k data repeatedly by issuing FIR_OP_RB/FIR_OP_WB and save them to a large buffer.

---
based on 'master' branch of Wolfgang's tree.

 drivers/mtd/nand/fsl_elbc_nand.c |  225 ++++++++++++++++++++++++++++++++++---
 1 files changed, 206 insertions(+), 19 deletions(-)
drivers/mtd/nand/fsl_elbc_nand.c +206 -19
index 4d1e527..6d0639a 100644
@@ -64,7 +64,7 @@ struct fsl_elbc_mtd {
 	struct device *dev;
 	int bank;               /* Chip select bank number           */
 	u8 __iomem *vbase;      /* Chip select base virtual address  */
-	int page_size;          /* NAND page size (0=512, 1=2048)    */
+	int page_size;          /* NAND page size (0=512, 1=2048, 2=4096,...) */
 	unsigned int fmr;       /* FCM Flash Mode Register value     */
 };
 
@@ -85,6 +85,8 @@ struct fsl_elbc_ctrl {
 	unsigned int mdr;        /* UPM/FCM Data Register value           */
 	unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
 	unsigned int oob;        /* Non zero if operating on OOB data     */
+	char *buffer;            /* just used when pagesize is greater    */
+				 /* than FCM RAM 2K limitation		  */
 };
 
 /* These map to the positions used by the FCM hardware ECC generator */
@@ -159,6 +161,44 @@ static struct nand_bbt_descr bbt_mirror_descr = {
 	.pattern = mirror_pattern,
 };
 
+static void io_to_buffer(struct mtd_info *mtd, int subpage, int oob)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	void *src, *dst;
+	int len = (oob ? 64 : 2048);
+
+	/* for emulating 4096+ bytes NAND using 2048-byte FCM RAM */
+	if (oob)
+		dst = ctrl->buffer + mtd->writesize + subpage * 64;
+	else
+		dst = ctrl->buffer + subpage * 2048;
+
+	src = ctrl->addr + (oob ? 2048 : 0);
+	memcpy_fromio(dst, src, len);
+}
+
+static void buffer_to_io(struct mtd_info *mtd, int subpage, int oob)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	void *src, *dst;
+	int len = (oob ? 64 : 2048);
+
+	if (oob)
+		src = ctrl->buffer + mtd->writesize + subpage * 64;
+	else
+		src = ctrl->buffer + subpage * 2048;
+
+	dst = ctrl->addr + (oob ? 2048 : 0);
+
+	memcpy_toio(dst, src, len);
+	/* See the in_8() in fsl_elbc_write_buf() */
+	in_8(ctrl->addr);
+}
+
 /*=================================*/
 
 /*
@@ -194,7 +234,7 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
 
 	/* for OOB data point to the second half of the buffer */
 	if (oob)
-		ctrl->index += priv->page_size ? 2048 : 512;
+		ctrl->index += mtd->writesize;
 
 	vdbg("set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "
 	     "index %x, pes %d ps %d\n",
@@ -295,6 +335,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 	struct fsl_elbc_mtd *priv = chip->priv;
 	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
 	fsl_lbc_t *lbc = ctrl->regs;
+	int i;
 
 	ctrl->use_mdr = 0;
 
@@ -321,6 +362,25 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
 		fsl_elbc_do_read(chip, 0);
 		fsl_elbc_run_command(mtd);
+
+		if (priv->page_size <= 1)
+			return;
+
+		/* Continue to read the rest bytes if writesize > 2048 */
+		io_to_buffer(mtd, 0, 0);
+		io_to_buffer(mtd, 0, 1);
+		/*
+		 * Maybe there are some reasons of FCM hardware timming,
+		 * we must insert a FIR_OP_NOP(0x00) before FIR_OP_RB.
+		 */
+		out_be32(&lbc->fir, FIR_OP_RB << FIR_OP1_SHIFT);
+
+		for (i = 1; i < priv->page_size; i++) {
+			fsl_elbc_run_command(mtd);
+			io_to_buffer(mtd, i, 0);
+			io_to_buffer(mtd, i, 1);
+		}
+
 		return;
 
 	/* READOOB reads only the OOB because no ECC is performed. */
@@ -328,14 +388,34 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		vdbg("fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
 		     " 0x%x, column: 0x%x.\n", page_addr, column);
 
-		out_be32(&lbc->fbcr, mtd->oobsize - column);
-		set_addr(mtd, column, page_addr, 1);
+		if (priv->page_size <= 1) {
+			out_be32(&lbc->fbcr, mtd->oobsize - column);
+			set_addr(mtd, column, page_addr, 1);
+		} else {
+			out_be32(&lbc->fbcr, 64);
+			set_addr(mtd, 0, page_addr, 1);
+			ctrl->index += column;
+		}
 
 		ctrl->read_bytes = mtd->writesize + mtd->oobsize;
-
 		fsl_elbc_do_read(chip, 1);
 		fsl_elbc_run_command(mtd);
 
+		if (priv->page_size <= 1)
+			return;
+
+		if (column < 64)
+			io_to_buffer(mtd, 0, 1);
+
+		out_be32(&lbc->fpar, in_be32(&lbc->fpar) & ~FPAR_LP_MS);
+		out_be32(&lbc->fir, FIR_OP_RB << FIR_OP1_SHIFT);
+		out_be32(&lbc->fbcr, 2112);
+		for (i = 1; i < priv->page_size; i++) {
+			fsl_elbc_run_command(mtd);
+			if (column < (64 * (i + 1)))
+				io_to_buffer(mtd, i, 1);
+		}
+
 		return;
 
 	/* READID must read all 5 possible bytes while CEB is active */
@@ -392,7 +472,18 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		ctrl->column = column;
 		ctrl->oob = 0;
 
-		if (priv->page_size) {
+		if (priv->page_size > 1) {
+			/* writesize > 2048 */
+			fcr = (NAND_CMD_STATUS   << FCR_CMD1_SHIFT) |
+				(NAND_CMD_SEQIN    << FCR_CMD2_SHIFT) |
+				(NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT);
+
+			out_be32(&lbc->fir,
+				(FIR_OP_CM2 << FIR_OP0_SHIFT) |
+				(FIR_OP_CA  << FIR_OP1_SHIFT) |
+				(FIR_OP_PA  << FIR_OP2_SHIFT) |
+				(FIR_OP_WB  << FIR_OP3_SHIFT));
+		} else if (priv->page_size) {
 			fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) |
 			      (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT);
 
@@ -429,12 +520,19 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		}
 
 		out_be32(&lbc->fcr, fcr);
-		set_addr(mtd, column, page_addr, ctrl->oob);
+		if (column >= mtd->writesize && priv->page_size > 1) {
+			/* for write oob && writesize > 2048 */
+			set_addr(mtd, 0, page_addr, 0);
+			ctrl->index = column;
+		} else {
+			set_addr(mtd, column, page_addr, ctrl->oob);
+		}
 		return;
 	}
 
 	/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
 	case NAND_CMD_PAGEPROG: {
+		int len;
 		vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
 		     "writing %d bytes.\n", ctrl->index);
 
@@ -442,14 +540,56 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		 * then set the exact length, otherwise use a full page
 		 * write so the HW generates the ECC.
 		 */
-		if (ctrl->oob || ctrl->column != 0 ||
-		    ctrl->index != mtd->writesize + mtd->oobsize)
+		if (ctrl->column >= mtd->writesize) {
+			/* write oob */
+			if (priv->page_size > 1) {
+				/* when pagesize of chip is greater than 2048,
+				 * we have to write full page to write spare
+				 * region, so we fill '0xff' to main region
+				 * and some bytes of spare region which we
+				 * don't want to rewrite.
+				 * (write '1' won't change the original value)
+				 */
+				memset(ctrl->buffer, 0xff, ctrl->column);
+				len = 2112;
+			} else {
+				len = mtd->writesize + mtd->oobsize -
+					ctrl->column;
+			}
+			out_be32(&lbc->fbcr, len);
+		} else if (ctrl->column != 0 ||
+			ctrl->index != mtd->writesize + mtd->oobsize) {
 			out_be32(&lbc->fbcr, ctrl->index);
-		else
+		} else {
 			out_be32(&lbc->fbcr, 0);
+		}
+
+		if (priv->page_size > 1) {
+			buffer_to_io(mtd, 0, 0);
+			buffer_to_io(mtd, 0, 1);
+		}
 
 		fsl_elbc_run_command(mtd);
 
+		if (priv->page_size <= 1)
+			return;
+
+		out_be32(&lbc->fir, FIR_OP_WB << FIR_OP1_SHIFT);
+		for (i = 1; i < priv->page_size; i++) {
+			ctrl->use_mdr = 1;
+			/* For the last subpage */
+			if (i == priv->page_size - 1) {
+				out_be32(&lbc->fir,
+					(FIR_OP_WB  << FIR_OP1_SHIFT) |
+					(FIR_OP_CM3 << FIR_OP2_SHIFT) |
+					(FIR_OP_CW1 << FIR_OP3_SHIFT) |
+					(FIR_OP_RS  << FIR_OP4_SHIFT));
+			}
+			buffer_to_io(mtd, i, 0);
+			buffer_to_io(mtd, i, 1);
+			fsl_elbc_run_command(mtd);
+		}
+
 		return;
 	}
 
@@ -516,7 +656,11 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
 		len = bufsize - ctrl->index;
 	}
 
-	memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
+	if (mtd->writesize > 2048)
+		memcpy(&ctrl->buffer[ctrl->index], buf, len);
+	else
+		memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
+
 	/*
 	 * This is workaround for the weird elbc hangs during nand write,
 	 * Scott Wood says: "...perhaps difference in how long it takes a
@@ -561,7 +705,10 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
 		return;
 
 	avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
-	memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
+	if (mtd->writesize > 2048)
+		memcpy(buf, &ctrl->buffer[ctrl->index], avail);
+	else
+		memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
 	ctrl->index += avail;
 
 	if (len > avail)
@@ -595,9 +742,15 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd,
 		return -EINVAL;
 	}
 
-	for (i = 0; i < len; i++)
-		if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
-			break;
+	if (mtd->writesize > 2048) {
+		for (i = 0; i < len; i++)
+			if (ctrl->buffer[ctrl->index + i] != buf[i])
+				break;
+	} else {
+		for (i = 0; i < len; i++)
+			if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
+				break;
+	}
 
 	ctrl->index += len;
 	return i == len && ctrl->status == LTESR_CC ? 0 : -EIO;
@@ -663,11 +816,11 @@ static void fsl_elbc_write_page(struct mtd_info *mtd,
 
 static struct fsl_elbc_ctrl *elbc_ctrl;
 
-static void fsl_elbc_ctrl_init(void)
+static int fsl_elbc_ctrl_init(void)
 {
 	elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL);
 	if (!elbc_ctrl)
-		return;
+		return -ENOMEM;
 
 	elbc_ctrl->regs = LBC_BASE_ADDR;
 
@@ -681,6 +834,20 @@ static void fsl_elbc_ctrl_init(void)
 	elbc_ctrl->read_bytes = 0;
 	elbc_ctrl->index = 0;
 	elbc_ctrl->addr = NULL;
+
+	/*
+	 * Freescale FCM controller has a 2K size limitation of buffer
+	 * RAM, so elbc_fcm_ctrl->buffer have to be used if writesize
+	 * of chip is greater than 2048.
+	 * We malloc a large enough buffer at this point, because we
+	 * don't know writesize before calling nand_scan(). We will
+	 * re-malloc later if needed.
+	 */
+	elbc_ctrl->buffer = kmalloc(8 * (2048 + 64), GFP_KERNEL);
+	if (!elbc_ctrl->buffer)
+		return -ENOMEM;
+
+	return 0;
 }
 
 int board_nand_init(struct nand_chip *nand)
@@ -689,8 +856,7 @@ int board_nand_init(struct nand_chip *nand)
 	uint32_t br = 0, or = 0;
 
 	if (!elbc_ctrl) {
-		fsl_elbc_ctrl_init();
-		if (!elbc_ctrl)
+		if (fsl_elbc_ctrl_init())
 			return -1;
 	}
 
@@ -793,3 +959,24 @@ int board_nand_init(struct nand_chip *nand)
 
 	return 0;
 }
+
+
+int board_nand_init_tail(struct mtd_info *mtd, struct nand_chip *nand)
+{
+	struct fsl_elbc_mtd *priv = nand->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+
+	kfree(ctrl->buffer);
+	ctrl->buffer = NULL;
+
+	if (mtd->writesize > 2048) {
+		priv->page_size = mtd->writesize >> 11;
+
+		ctrl->buffer = kmalloc(mtd->writesize + mtd->oobsize,
+						GFP_KERNEL);
+		if (!ctrl->buffer)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
Signature · 1 line
1.6.4
#2 lore raw

PATCH 3/3 mtd/nand: Add NAND chip ID to support Micron 4K pagesize NAND chip

Add NAND chip ID 0x38 in ids table to support Micron 4K pagesize NAND chip.

---
based on 'master' branch of Wolfgang's tree.

 drivers/mtd/nand/nand_ids.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
drivers/mtd/nand/nand_ids.c +1 -0
index 8d7ea76..42acc28 100644
@@ -102,6 +102,7 @@ const struct nand_flash_dev nand_flash_ids[] = {
 	/* 8 Gigabit */
 	{"NAND 1GiB 1,8V 8-bit",	0xA3, 0, 1024, 0, LP_OPTIONS},
 	{"NAND 1GiB 3,3V 8-bit",	0xD3, 0, 1024, 0, LP_OPTIONS},
+	{"NAND 1GiB 3,3V 8-bit",	0x38, 0, 1024, 0, LP_OPTIONS},
 	{"NAND 1GiB 1,8V 16-bit",	0xB3, 0, 1024, 0, LP_OPTIONS16},
 	{"NAND 1GiB 3,3V 16-bit",	0xC3, 0, 1024, 0, LP_OPTIONS16},
 
Signature · 1 line
1.6.4
#3 lore raw

PATCH 2/3 mtd/nand : workaround for Freescale FCM to support 4k pagesize Nand chip

In reply to Shengzhou Liu
Quoted 5 lines · Shengzhou Liu

Freescale FCM controller has a 2K size limitation of buffer RAM. In order to support the Nand flash chip whose page size is larger than 2K bytes, we read/write 2k data repeatedly by issuing FIR_OP_RB/FIR_OP_WB and save them to a large buffer.

Quoted 8 lines

---
based on 'master' branch of Wolfgang's tree.

 drivers/mtd/nand/fsl_elbc_nand.c |  225 ++++++++++++++++++++++++++++++++++---
 1 files changed, 206 insertions(+), 19 deletions(-)

Please see my feedback on the Linux version of this patch:
http://lists.ozlabs.org/pipermail/linuxppc-dev/2011-November/094199.html

-Scott

#4 lore raw

PATCH 1/3 mtd/nand : Add function board_nand_init_tail() for some special NAND controllers

In reply to Shengzhou Liu
Quoted 6 lines · On Tuesday 22 November 2011 03:54:14 Shengzhou Liu wrote:

In some NAND controllers there is a size limitation of RAM buffer(2K bytes). To support large-page NAND chips with greater than 2K pagesize, we need a large buffer, but we don't know pagesize before calling nand_scan_ident(), for more flexible and to identify different cases of large-page greater than 2K bytes, we have a board_nand_init_tail() between nand_scan_ident() and nand_scan_tail().

iirc, newer Linux mtd tree handles this better by breaking up the code paths so individual drivers can call things in the right order. perhaps we should update our mtd stack so we can do that ?
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20111122/9a3ca21e/attachment.pgp>

#5 lore raw

PATCH 1/3 mtd/nand : Add function board_nand_init_tail() for some special NAND controllers

Quoted 11 lines · Mike Frysinger
Quoted 6 lines · On Tuesday 22 November 2011 03:54:14 Shengzhou Liu wrote:

In some NAND controllers there is a size limitation of RAM buffer(2K bytes). To support large-page NAND chips with greater than 2K pagesize, we need a large buffer, but we don't know pagesize before calling nand_scan_ident(), for more flexible and to identify different cases of large-page greater than 2K bytes, we have a board_nand_init_tail() between nand_scan_ident() and nand_scan_tail().

iirc, newer Linux mtd tree handles this better by breaking up the code paths so individual drivers can call things in the right order. perhaps we should update our mtd stack so we can do that ?

We already have that on the mtd side -- this patch uses it. The issue is the U-Boot glue code calling nand_scan() rather than letting the drivers control the process.

This patch is less intrusive than changing all the drivers, but if someone wants to actually do that (without breaking anything), or more realistically set up a transition mechanism, that'd be great. :-)

-Scott

#6 lore raw

PATCH 1/3 mtd/nand : Add function board_nand_init_tail() for some special NAND controllers

Quoted 13 lines · On Tuesday 22 November 2011 16:11:20 Scott Wood wrote:
Quoted 11 lines · Mike Frysinger
Quoted 6 lines · On Tuesday 22 November 2011 03:54:14 Shengzhou Liu wrote:

In some NAND controllers there is a size limitation of RAM buffer(2K bytes). To support large-page NAND chips with greater than 2K pagesize, we need a large buffer, but we don't know pagesize before calling nand_scan_ident(), for more flexible and to identify different cases of large-page greater than 2K bytes, we have a board_nand_init_tail() between nand_scan_ident() and nand_scan_tail().

iirc, newer Linux mtd tree handles this better by breaking up the code paths so individual drivers can call things in the right order. perhaps we should update our mtd stack so we can do that ?

Quoted 7 lines

We already have that on the mtd side -- this patch uses it. The issue is the U-Boot glue code calling nand_scan() rather than letting the drivers control the process.

This patch is less intrusive than changing all the drivers, but if someone wants to actually do that (without breaking anything), or more realistically set up a transition mechanism, that'd be great. :-)

ok, so it is a short coming in the current u-boot mtd framework that we should look at migrating away from. no, i'm not volunteering to take this on :). -mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20111123/dafe6222/attachment.pgp>