diff mbox

[U-Boot,RFC,1/8] NAND: nand_spl/nand_boot.c: add 16-bit and readid support

Message ID 1293497228-15911-2-git-send-email-john.rigby@linaro.org
State Changes Requested
Headers show

Commit Message

John Rigby Dec. 28, 2010, 12:47 a.m. UTC
Some platforms read the nand type to make configuration
choices.  For example, some versions of OMAP3 Beagle use
the NAND type as a hint of the DRAM type.

Turn readid support on with CONFIG_SYS_NAND_BOOT_READID

Add 16-bit nand support.
Turn it on with CONFIG_SYS_NAND_BUSWIDTH_16

Signed-off-by: John Rigby <john.rigby@linaro.org>
CC: Scott Wood <scootwood@freescale.com>
---
 include/nand.h       |    3 +
 nand_spl/nand_boot.c |  134 ++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 112 insertions(+), 25 deletions(-)

Comments

Scott Wood Jan. 4, 2011, 10:08 p.m. UTC | #1
On Mon, Dec 27, 2010 at 05:47:01PM -0700, John Rigby wrote:
> Some platforms read the nand type to make configuration
> choices.  For example, some versions of OMAP3 Beagle use
> the NAND type as a hint of the DRAM type.

That seems rather hacky... are you sure it makes sense to use a single
U-Boot image across that set of boards?

> diff --git a/nand_spl/nand_boot.c b/nand_spl/nand_boot.c
> index 76b8566..1ae2cd0 100644
> --- a/nand_spl/nand_boot.c
> +++ b/nand_spl/nand_boot.c
> @@ -27,6 +27,15 @@
> 
>  static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
> 
> +static uint8_t nand_read_byte(struct nand_chip *chip)
> +{
> +#ifndef CONFIG_SYS_NAND_BUSWIDTH_16
> +       return readb(chip->IO_ADDR_R);
> +#else
> +       return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
> +#endif

Are the endian assumptions in the above appropriate for all hardware?

Converting something away from CPU endianness and then using it as native
seems wrong in theory, even if in practice the right bytes end up in the
right place.

Unfortunately there seem to be no arch-neutral non-raw native-endian or
big-endian I/O accessors in U-Boot.  For that matter, we don't know if the
NAND controller is native endian or not (e.g.  a PCI-based NAND controller
in a big-endian system).

I suggest defining
CONFIG_SYS_NAND_BUSWIDTH_16LE/CONFIG_SYS_NAND_BUSWIDTH_16BE, and
implementing out_be16/in_be16/out_le16/in_le16 on each architecture that
needs to use this.

> +#ifndef CONFIG_SYS_NAND_BOOT_ECC_SCRATCH
> +#define CONFIG_SYS_NAND_BOOT_ECC_SCRATCH 0x10000
> +#endif

Where is this documented or used?

>  /*
> + * Get ready for booting from NAND.  This is for platforms
> + * that need to read nand data or nand chip id's before initializing
> + * SDRAM.
> + */
> +void nand_boot_init(struct nand_chip *nand_chip, nand_info_t (*nand_info))
> +{
> +       /*
> +        * Init board specific nand support
> +        */
> +       nand_chip->select_chip = NULL;
> +       nand_info->priv = nand_chip;
> +       nand_chip->IO_ADDR_R = nand_chip->IO_ADDR_W = (void  __iomem *)CONFIG_SYS_NAND_BASE;

Line length.

> +       nand_chip->dev_ready = NULL;    /* preset to NULL */
> +       board_nand_init(nand_chip);
> +
> +       if (nand_chip->select_chip)
> +               nand_chip->select_chip(nand_info, 0);
> +
> +}

No newline at end of block.

-Scott
Scott Wood Jan. 4, 2011, 10:19 p.m. UTC | #2
On Tue, Jan 04, 2011 at 04:08:29PM -0600, Scott Wood wrote:
> On Mon, Dec 27, 2010 at 05:47:01PM -0700, John Rigby wrote:
> > diff --git a/nand_spl/nand_boot.c b/nand_spl/nand_boot.c
> > index 76b8566..1ae2cd0 100644
> > --- a/nand_spl/nand_boot.c
> > +++ b/nand_spl/nand_boot.c
> > @@ -27,6 +27,15 @@
> > 
> >  static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
> > 
> > +static uint8_t nand_read_byte(struct nand_chip *chip)
> > +{
> > +#ifndef CONFIG_SYS_NAND_BUSWIDTH_16
> > +       return readb(chip->IO_ADDR_R);
> > +#else
> > +       return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
> > +#endif

I missed that this is a new function, not just adding 16-bit support.  This
should only be used as the default; if the driver provides its own read_byte
that should be used instead.

> Are the endian assumptions in the above appropriate for all hardware?

Hmm, it looks like the non-SPL NAND code already does it like this.  I guess
NAND endianness differing from native endianness just requires a custom
read_byte function.

-Scott
diff mbox

Patch

diff --git a/include/nand.h b/include/nand.h
index a452411..3c6237a 100644
--- a/include/nand.h
+++ b/include/nand.h
@@ -130,6 +130,9 @@  int nand_get_lock_status(nand_info_t *meminfo, loff_t offset);
 void board_nand_select_device(struct nand_chip *nand, int chip);
 #endif
 
+#ifdef CONFIG_SYS_NAND_BOOT_READID
+int nand_boot_readid(int *manf_id, int *dev_id);
+#endif
 __attribute__((noreturn)) void nand_boot(void);
 
 #endif
diff --git a/nand_spl/nand_boot.c b/nand_spl/nand_boot.c
index 76b8566..1ae2cd0 100644
--- a/nand_spl/nand_boot.c
+++ b/nand_spl/nand_boot.c
@@ -27,6 +27,15 @@ 
 
 static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
 
+static uint8_t nand_read_byte(struct nand_chip *chip)
+{
+#ifndef CONFIG_SYS_NAND_BUSWIDTH_16
+	return readb(chip->IO_ADDR_R);
+#else
+	return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
+#endif
+}
+
 #if (CONFIG_SYS_NAND_PAGE_SIZE <= 512)
 /*
  * NAND command for small page NAND devices (512)
@@ -46,6 +55,9 @@  static int nand_command(struct mtd_info *mtd, int block, int page, int offs, u8
 	this->cmd_ctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
 	/* Set ALE and clear CLE to start address cycle */
 	/* Column address */
+#ifdef CONFIG_SYS_NAND_BUSWIDTH_16
+	offs >>= 1;
+#endif
 	this->cmd_ctrl(mtd, offs, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
 	this->cmd_ctrl(mtd, page_addr & 0xff, NAND_CTRL_ALE); /* A[16:9] */
 	this->cmd_ctrl(mtd, (page_addr >> 8) & 0xff,
@@ -94,6 +106,9 @@  static int nand_command(struct mtd_info *mtd, int block, int page, int offs, u8
 	this->cmd_ctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
 	/* Set ALE and clear CLE to start address cycle */
 	/* Column address */
+#ifdef CONFIG_SYS_NAND_BUSWIDTH_16
+	offs >>= 1;
+#endif
 	this->cmd_ctrl(mtd, offs & 0xff,
 		       NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */
 	this->cmd_ctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */
@@ -128,17 +143,27 @@  static int nand_is_bad_block(struct mtd_info *mtd, int block)
 {
 	struct nand_chip *this = mtd->priv;
 
+#ifndef CONFIG_SYS_NAND_BUSWIDTH_16
+	bad = 0;
 	nand_command(mtd, block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, NAND_CMD_READOOB);
-
-	/*
-	 * Read one byte
-	 */
 	if (readb(this->IO_ADDR_R) != 0xff)
 		return 1;
-
+#else
+	u16 bad;
+	nand_command(mtd, block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS & 0xFE, NAND_CMD_READOOB);
+	bad = cpu_to_le16(readw(this->IO_ADDR_R));
+	if (CONFIG_SYS_NAND_BAD_BLOCK_POS & 0x1)
+		bad >> 8;
+	if ((bad & 0xff) != 0xff)
+		return 1;
+#endif
 	return 0;
 }
 
+#ifndef CONFIG_SYS_NAND_BOOT_ECC_SCRATCH
+#define CONFIG_SYS_NAND_BOOT_ECC_SCRATCH 0x10000
+#endif
+
 static int nand_read_page(struct mtd_info *mtd, int block, int page, uchar *dst)
 {
 	struct nand_chip *this = mtd->priv;
@@ -222,47 +247,54 @@  static int nand_load(struct mtd_info *mtd, unsigned int offs,
 }
 
 /*
+ * Get ready for booting from NAND.  This is for platforms
+ * that need to read nand data or nand chip id's before initializing
+ * SDRAM.
+ */
+void nand_boot_init(struct nand_chip *nand_chip, nand_info_t (*nand_info))
+{
+	/*
+	 * Init board specific nand support
+	 */
+	nand_chip->select_chip = NULL;
+	nand_info->priv = nand_chip;
+	nand_chip->IO_ADDR_R = nand_chip->IO_ADDR_W = (void  __iomem *)CONFIG_SYS_NAND_BASE;
+	nand_chip->dev_ready = NULL;	/* preset to NULL */
+	board_nand_init(nand_chip);
+
+	if (nand_chip->select_chip)
+		nand_chip->select_chip(nand_info, 0);
+
+}
+
+/*
  * The main entry for NAND booting. It's necessary that SDRAM is already
  * configured and available since this code loads the main U-Boot image
  * from NAND into SDRAM and starts it from there.
  */
-void nand_boot(void)
+void nand_boot_tail(struct nand_chip *nand_chip, nand_info_t *nand_info)
 {
-	struct nand_chip nand_chip;
-	nand_info_t nand_info;
 	int ret;
 	__attribute__((noreturn)) void (*uboot)(void);
 
 	/*
-	 * Init board specific nand support
-	 */
-	nand_chip.select_chip = NULL;
-	nand_info.priv = &nand_chip;
-	nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = (void  __iomem *)CONFIG_SYS_NAND_BASE;
-	nand_chip.dev_ready = NULL;	/* preset to NULL */
-	board_nand_init(&nand_chip);
-
-	if (nand_chip.select_chip)
-		nand_chip.select_chip(&nand_info, 0);
-
-	/*
 	 * Load U-Boot image from NAND into RAM
 	 */
-	ret = nand_load(&nand_info, CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE,
+	ret = nand_load(nand_info, CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE,
 			(uchar *)CONFIG_SYS_NAND_U_BOOT_DST);
 
 #ifdef CONFIG_NAND_ENV_DST
-	nand_load(&nand_info, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
+	nand_load(nand_info, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
 		  (uchar *)CONFIG_NAND_ENV_DST);
 
 #ifdef CONFIG_ENV_OFFSET_REDUND
-	nand_load(&nand_info, CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
+	nand_load(nand_info, CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
 		  (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
 #endif
 #endif
 
-	if (nand_chip.select_chip)
-		nand_chip.select_chip(&nand_info, -1);
+	if (nand_chip->select_chip)
+		nand_chip->select_chip(nand_info, -1);
 
 	/*
 	 * Jump to U-Boot image
@@ -270,3 +302,55 @@  void nand_boot(void)
 	uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
 	(*uboot)();
 }
+
+#ifdef CONFIG_SYS_NAND_BOOT_READID
+int nand_boot_readid(int *manf_id, int *dev_id)
+{
+	struct nand_chip nand_chip;
+	nand_info_t nand_info;
+
+	nand_boot_init(&nand_chip, &nand_info);
+
+	/*
+	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
+	 * after power-up
+	 */
+	nand_chip.cmd_ctrl(&nand_info, NAND_CMD_RESET, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+
+	if (!nand_chip.dev_ready) {
+		CONFIG_SYS_NAND_READ_DELAY;
+		nand_chip.cmd_ctrl(&nand_info, NAND_CMD_STATUS, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+		nand_chip.cmd_ctrl(&nand_info, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+		while (!(nand_read_byte(&nand_chip) & NAND_STATUS_READY))
+			CONFIG_SYS_NAND_READ_DELAY;
+	}
+
+	/* Send the command for reading device ID */
+	nand_chip.cmd_ctrl(&nand_info, NAND_CMD_READID, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+	nand_chip.cmd_ctrl(&nand_info, 0x0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+	nand_chip.cmd_ctrl(&nand_info, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+
+	/* Read manufacturer and device IDs */
+	*manf_id = nand_read_byte(&nand_chip);
+	*dev_id = nand_read_byte(&nand_chip);
+
+	if (nand_chip.select_chip)
+		nand_chip.select_chip(&nand_info, -1);
+
+	return 0;
+}
+#endif
+
+
+/*
+ * Init the nand subsystem and boot.
+ */
+void nand_boot(void)
+{
+	struct nand_chip nand_chip;
+	nand_info_t nand_info;
+
+	nand_boot_init(&nand_chip, &nand_info);
+	nand_boot_tail(&nand_chip, &nand_info);
+}