diff mbox

[U-Boot,2/2] mtd/nand : workaround for Freescale FCM to supportlarge-page Nand chip

Message ID F87D23B7E1F84E4AB52E4FA4A55F85DC0262CC5F@tpamail.elutions.com
State Superseded
Delegated to: Scott Wood
Headers show

Commit Message

Mike Hench July 6, 2011, 12:04 a.m. UTC
It works. It is awesome to be able to do this.
If you are interested, and this isn't pretty
This a patch against 2010-06 uboot.
(been busy, but this was too good to ignore)

I will try to find some time to make it better
But for now comments are appreciated.
The Makefile and environment variable hack is ugly.

I Was going to add error checking and 2 copies of u-boot
(if a page fails, get it from the second copy)
This has an additional hack to fake the subpages.

diff -purN orig/drivers/mtd/nand/fsl_elbc_nand.c
u-boot-2010.06/drivers/mtd/nand/fsl_elbc_nand.c
--- orig/drivers/mtd/nand/fsl_elbc_nand.c	2011-06-30
14:11:27.304294055 -0500
+++ u-boot-2010.06/drivers/mtd/nand/fsl_elbc_nand.c	2011-06-30
14:10:46.880516050 -0500
@@ -86,6 +86,10 @@ struct fsl_elbc_ctrl {
 	unsigned int use_mdr;    /* Non zero if the MDR is to be set
*/
 	unsigned int oob;        /* Non zero if operating on OOB data
*/
 	uint8_t *oob_poi;        /* Place to write ECC after read back
*/
+
+	int subpage_shift;       /* If writesize > 2048, these two
members*/
+	int subpage_mask;        /* are used to calculate the real page
*/
+                            /* address and real column address       */
 };
 
 /* These map to the positions used by the FCM hardware ECC generator */
@@ -173,10 +177,20 @@ static void set_addr(struct mtd_info *mt
 	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
 	fsl_lbus_t *lbc = ctrl->regs;
 	int buf_num;
+	u32 real_ca = column;
+
+	if (priv->page_size && ctrl->subpage_shift) {
+		real_ca = (page_addr & ctrl->subpage_mask) * 2112;
+		page_addr >>= ctrl->subpage_shift;
+	}
 
 	ctrl->page = page_addr;
 
 	if (priv->page_size) {
+		real_ca += (oob ? 2048 : 0);
+		ctrl->use_mdr = 1;
+		ctrl->mdr = real_ca;
+
 		out_be32(&lbc->fbar, page_addr >> 6);
 		out_be32(&lbc->fpar,
 			 ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI)
|
@@ -265,11 +279,12 @@ static void fsl_elbc_do_read(struct nand
 
 	if (priv->page_size) {
 		out_be32(&lbc->fir,
-			 (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-			 (FIR_OP_CA  << FIR_OP1_SHIFT) |
-			 (FIR_OP_PA  << FIR_OP2_SHIFT) |
-			 (FIR_OP_CW1 << FIR_OP3_SHIFT) |
-			 (FIR_OP_RBW << FIR_OP4_SHIFT));
+			(FIR_OP_CW0 << FIR_OP0_SHIFT) |
+			(FIR_OP_UA  << FIR_OP1_SHIFT) |
+			(FIR_OP_UA  << FIR_OP2_SHIFT) |
+			(FIR_OP_PA  << FIR_OP3_SHIFT) |
+			(FIR_OP_CW1 << FIR_OP4_SHIFT) |
+			(FIR_OP_RBW << FIR_OP5_SHIFT));
 
 		out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
 				    (NAND_CMD_READSTART <<
FCR_CMD1_SHIFT));
@@ -399,10 +414,11 @@ static void fsl_elbc_cmdfunc(struct mtd_
 
 			out_be32(&lbc->fir,
 				 (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-				 (FIR_OP_CA  << FIR_OP1_SHIFT) |
-				 (FIR_OP_PA  << FIR_OP2_SHIFT) |
-				 (FIR_OP_WB  << FIR_OP3_SHIFT) |
-				 (FIR_OP_CW1 << FIR_OP4_SHIFT));
+				 (FIR_OP_UA  << FIR_OP1_SHIFT) |
+				 (FIR_OP_UA  << FIR_OP2_SHIFT) |
+				 (FIR_OP_PA  << FIR_OP3_SHIFT) |
+				 (FIR_OP_WB  << FIR_OP4_SHIFT) |
+				 (FIR_OP_CW1 << FIR_OP5_SHIFT));
 		} else {
 			fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) |
 			      (NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
@@ -453,6 +469,10 @@ static void fsl_elbc_cmdfunc(struct mtd_
 			full_page = 1;
 		}
 
+		if (priv->page_size)
+			ctrl->use_mdr = 1;
+
+
 		fsl_elbc_run_command(mtd);
 
 		ctrl->oob_poi = NULL;
@@ -808,3 +828,29 @@ int board_nand_init(struct nand_chip *na
 
 	return 0;
 }
+
+int board_nand_init_tail(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+
+	/* Hack for supporting the flash chip whose writesize is
+	 * larger than 2K bytes.
+	 */
+	if (mtd->writesize > 2048) {
+		ctrl->subpage_shift = ffs(mtd->writesize >> 11) - 1;
+		ctrl->subpage_mask =
+			(1 << ctrl->subpage_shift) - 1;
+		/* Rewrite mtd->writesize, mtd->oobsize,
chip->page_shift
+		 * and chip->pagemask.
+		 */
+		mtd->writesize = 2048;
+		mtd->oobsize = 64;
+		chip->page_shift = ffs(mtd->writesize) - 1;
+		chip->pagemask = (chip->chipsize >> chip->page_shift) -
1;
+		mtd->subpage_sft--;
+	}
+}
+
+
diff -purN orig/drivers/mtd/nand/nand_base.c
u-boot-2010.06/drivers/mtd/nand/nand_base.c
--- orig/drivers/mtd/nand/nand_base.c	2011-06-30 14:11:27.408242045
-0500
+++ u-boot-2010.06/drivers/mtd/nand/nand_base.c	2011-06-30
14:10:46.920496045 -0500
@@ -2998,6 +2998,7 @@ int nand_scan_tail(struct mtd_info *mtd)
 		}
 	}
 	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
+printf("sps=%d\n", chip->subpagesize);
 
 	/* Initialize state */
 	chip->state = FL_READY;
diff -purN orig/drivers/mtd/nand/nand.c
u-boot-2010.06/drivers/mtd/nand/nand.c
--- orig/drivers/mtd/nand/nand.c	2011-06-30 14:11:27.452220050
-0500
+++ u-boot-2010.06/drivers/mtd/nand/nand.c	2011-06-30
14:10:46.948482047 -0500
@@ -74,6 +74,9 @@ static void nand_init_chip(struct mtd_in
 		mtd->name = NULL;
 		mtd->size = 0;
 	}
+#ifdef CONFIG_SYS_NAND_INIT_TAIL
+	board_nand_init_tail(mtd);
+#endif
 
 }
 
diff -purN orig/include/nand.h u-boot-2010.06/include/nand.h
--- orig/include/nand.h	2010-06-29 16:28:28.000000000 -0500
+++ u-boot-2010.06/include/nand.h	2011-06-30 14:10:46.888512049
-0500
@@ -31,7 +31,9 @@ extern void nand_init(void);
 #include <linux/mtd/nand.h>
 
 extern int board_nand_init(struct nand_chip *nand);
-
+#ifdef CONFIG_SYS_NAND_INIT_TAIL
+extern int board_nand_init_tail(struct mtd_info *mtd);
+#endif
 typedef struct mtd_info nand_info_t;
 
 extern int nand_curr_device;
 $(ONENAND_IPL):	$(TIMESTAMP_FILE) $(VERSION_FILE)
$(obj)include/autoconf.mk
 		$(MAKE) -C onenand_ipl/board/$(BOARDDIR) all
diff -purN orig/nand_spl/board/freescale/mpc8313erdb/Makefile
u-boot-2010.06/nand_spl/board/freescale/mpc8313erdb/Makefile
--- orig/nand_spl/board/freescale/mpc8313erdb/Makefile	2011-06-30
14:11:27.196348060 -0500
+++ u-boot-2010.06/nand_spl/board/freescale/mpc8313erdb/Makefile
2011-06-30 14:10:46.900506048 -0500
@@ -24,7 +24,17 @@
 
 NAND_SPL := y
 TEXT_BASE := 0xfff00000
-PAD_TO := 0xfff04000
+
+NAND_BLOCK_SIZE ?= 128K
+NAND_PAGE_SIZE ?= 2K
+
+ifeq ($(NAND_BLOCK_SIZE),256K)
+PAD_TO := 0xfff40000
+endif
+ifeq ($(NAND_BLOCK_SIZE),128K)
+PAD_TO := 0xfff20000
+endif
+PAD_TO ?= 0xfff04000
 
 include $(TOPDIR)/config.mk
 
@@ -44,12 +54,21 @@ LNDIR	:= $(OBJTREE)/nand_spl/board/$(BOA
 
 nandobj	:= $(OBJTREE)/nand_spl/
 
-ALL	= $(nandobj)u-boot-spl $(nandobj)u-boot-spl.bin
$(nandobj)u-boot-spl-16k.bin
+ALL	= $(nandobj)u-boot-spl $(nandobj)u-boot-spl.bin
$(nandobj)u-boot-spl-pad.bin
 
 all:	$(obj).depend $(ALL)
 
-$(nandobj)u-boot-spl-16k.bin: $(nandobj)u-boot-spl
+$(nandobj)u-boot-spl-pad.bin: $(nandobj)u-boot-spl
 	$(OBJCOPY) ${OBJCFLAGS} --pad-to=$(PAD_TO) -O binary $< $@
+ifeq ($(NAND_PAGE_SIZE),4K)
+	dd if=$@ bs=2048 count=1 > p0
+	dd if=$@ bs=2048 count=1 skip=1 > p1
+	dd if=$@ bs=2048 count=1 skip=2 > p2
+	dd if=$@ bs=2048 count=1 skip=3 > p3
+	dd if=$@ bs=2048 skip=4 > p4
+	cat p0 p2 p1 p3 p4 > $@
+	rm -f p0 p1 p2 p3 p4
+endif
 
 $(nandobj)u-boot-spl.bin:	$(nandobj)u-boot-spl
 	$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
diff -purN orig/nand_spl/nand_boot_fsl_elbc.c
u-boot-2010.06/nand_spl/nand_boot_fsl_elbc.c
--- orig/nand_spl/nand_boot_fsl_elbc.c	2010-06-29 16:28:28.000000000
-0500
+++ u-boot-2010.06/nand_spl/nand_boot_fsl_elbc.c	2011-06-30
14:10:56.715596060 -0500
@@ -47,6 +47,59 @@ static void nand_wait(void)
 	}
 }
 
+#if CONFIG_SYS_NAND_PAGE_SIZE > 2048
+#define PAGE_MULT (CONFIG_SYS_NAND_PAGE_SIZE/2048)
+#define BLOCK_PAGES
(CONFIG_SYS_NAND_BLOCK_SIZE/CONFIG_SYS_NAND_PAGE_SIZE)
+static void nand_load(unsigned int offs, int uboot_size, uchar *dst)
+{
+	fsl_lbus_t *regs = (fsl_lbus_t *)(CONFIG_SYS_IMMR + 0x5000);
+	uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE;
+	int fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT) | 2;
+	int page_addr2k = offs/2048;
+	int j, page_in_block, col, buf_ofs;
+	int pos = 0;
+
+	fmr |= FMR_ECCM;
+	out_be32(&regs->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
+	                     (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
+	out_be32(&regs->fir,
+	         (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+	         (FIR_OP_UA  << FIR_OP1_SHIFT) |
+	         (FIR_OP_UA  << FIR_OP2_SHIFT) |
+	         (FIR_OP_PA  << FIR_OP3_SHIFT) |
+	         (FIR_OP_CW1 << FIR_OP4_SHIFT) |
+	         (FIR_OP_RBW << FIR_OP5_SHIFT));
+
+	out_be32(&regs->fbcr, 0);
+	clrsetbits_be32(&regs->bank[0].br, BR_DECC, BR_DECC_CHK_GEN);
+
+	while (pos < uboot_size) {
+		page_in_block = page_addr2k/PAGE_MULT & (BLOCK_PAGES-1);
+		col = (page_addr2k & (PAGE_MULT-1))*2112;
+
+		if(page_in_block == 0 && col == 0)
+			out_be32(&regs->fbar,
+
page_addr2k/(CONFIG_SYS_NAND_BLOCK_SIZE/2048)
+			);
+
+		out_be32(&regs->ltesr, ~0);
+		out_be32(&regs->lteatr, 0);
+		out_be32(&regs->fpar, page_in_block << 12);
+		out_be32(&regs->fmr, fmr);
+		out_be32(&regs->mdr, col);
+		out_be32(&regs->lsor, 0);
+		nand_wait();
+
+		buf_ofs = (page_in_block & 1) * 4096;
+
+		for (j = 0; j < 2048; j++)
+			dst[pos + j] = buf[buf_ofs + j];
+
+		page_addr2k++;
+		pos += 2048;
+	}
+}
+#else
 static void nand_load(unsigned int offs, int uboot_size, uchar *dst)
 {
 	fsl_lbus_t *regs = (fsl_lbus_t *)(CONFIG_SYS_IMMR + 0x5000);
@@ -122,6 +175,7 @@ static void nand_load(unsigned int offs,
 		} while ((offs & (block_size - 1)) && (pos <
uboot_size));
 	}
 }
+#endif
 
 /*
  * The main entry for NAND booting. It's necessary that SDRAM is
already


-----Original Message-----
From: Scott Wood [mailto:scottwood@freescale.com] 
Sent: Tuesday, July 05, 2011 6:13 PM
To: Matthew L. Creech
Cc: Mike Hench; b35362@freescale.com; u-boot@lists.denx.de
Subject: Re: [PATCH 2/2] mtd/nand : workaround for Freescale FCM to
supportlarge-page Nand chip

On Tue, 5 Jul 2011 19:08:21 -0400
"Matthew L. Creech" <mlcreech@gmail.com> wrote:

> On Tue, Jun 28, 2011 at 12:30 PM, Scott Wood <scottwood@freescale.com>
wrote:
> > On Tue, 28 Jun 2011 11:35:12 -0400
> > Mike Hench <mhench@elutions.com> wrote:
> >
> >>
> >> Any boot ideas ?
> >> Will the FCM load 2k and run it?
> >
> > The 4K boot region will have to be split over pages 0 and 2 (2k
view) or
> > the first half of pages 0 and 1 (4k view).
> >
> 
> (Redirecting to the U-Boot list)
> 
> Hi Scott,
> 
> Does this kind of page-splitting only apply to the IPL and nothing
> else?

Yes, because that's loaded directly by the hardware which doesn't
implement this workaround.

> If so, it seems that if:
> 
> 1. modifications are made to U-Boot's fsl_elbc_nand driver similar to
> Liu Shuo's kernel mods, and
> 2. an option is added to generate u-boot-nand.bin with its IPL split
> into two 2k chunks
> 
> then we can boot and run entirely from a 4k page device on MPC 83xx,
correct?

That's the plan, though I don't know whether it's been tried yet.

-Scott
diff mbox

Patch

diff -purN orig/Makefile u-boot-2010.06/Makefile
--- orig/Makefile	2010-06-29 16:28:28.000000000 -0500
+++ u-boot-2010.06/Makefile	2011-06-30 14:10:46.892510049 -0500
@@ -371,7 +371,7 @@  $(NAND_SPL):	$(TIMESTAMP_FILE) $(VERSION
 		$(MAKE) -C nand_spl/board/$(BOARDDIR) all
 
 $(U_BOOT_NAND):	$(NAND_SPL) $(obj)u-boot.bin
-		cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin >
$(obj)u-boot-nand.bin
+		cat $(obj)nand_spl/u-boot-spl-pad.bin $(obj)u-boot.bin >
$(obj)u-boot-nand.bin