diff mbox

[U-Boot,V7,4/7] spl: add NAND Library to new SPL

Message ID 1311958421-9607-5-git-send-email-simonschwarzcor@gmail.com
State Superseded
Headers show

Commit Message

Simon Schwarz July 29, 2011, 4:53 p.m. UTC
Adds NAND libarary to SPL.

Signed-off-by: Simon Schwarz <simonschwarzcor@gmail.com>
---
V1 changes:
CHG Default to HW ecc in SPL build
ADD nand_read_buf16 function, read buffer
ADD omap_dev_ready function, indicte if chip is ready

V2 changes:
DEL GPMC_WAIT0_PIN_ACTIVE define
CHG omap_dev_ready() renamed to  omap_spl_dev_ready(), does not use the
	GPMC_WAIT0_PIN_ACTIVE-define anymore
CHG ogpmc_read_buf16 renamed omap_spl_read_buf16
ADD omap_spl_read_buf, 8x buf read function
ADD CONFIG_SPL_POWER_SUPPORT and CONFIG_SPL_NAND_SUPPORT to SPL
CHG cosmetic
CHG nand_base and nand_bbt aren't needed for SPL anymore
CHG omap_nand_switch_ecc is not compiled for SPL
ADD entry for CONFIG_SPL_POWER_SUPPORT and CONFIG_SPL_NAND_SUPPORT to README.SPL

V3 changes:
DEL cosmetic (empty line)

V4 changes:
nothing

V5 changes:
CHG nand_ecc.o is only compiled for SPL if CONFIG_OMAP34XX is set

V6 changes:
ADD nand_spl.c - git add, finally
DEL nand_ecc barrier ifdef for OMAP3

V7 changes:
CHG nand_read_buf and nand_read_buf16 - removed static modifier
ADD nand_base.c to SPL
DEL omap_spl_read_buf16 and omap_spl_read_buf from omap_gpmc - now use the
	functions of nand_base.de
ADD nand_read_buf and nand_read_buf16 to nand.h
CHG commit message to reflect that also POWER library is added
CHG renamed nand_spl.c to nand_spl_simple.c
CHG removed mtd from all interfaces of the nand_spl_simple
CHG nand_load image is now nand_spl_load_image
CHG removed comment on the transition from nand_boot to nand_spl_simple
CHG all offset parameters are now of type loff_t
CHG moved power library adding to an own patch

Transition from V1 to V2 also includes that this patch is now based on
	- the new SPL layout by Aneesh V and Daniel Schwierzeck
	- the OMAP4 SPL patches by Aneesh V

This Patch is related to "[U-Boot,4/5] devkit8000 nand_spl: Add SPL NAND support
to omap_gpmc driver"
(http://article.gmane.org/gmane.comp.boot-loaders.u-boot/102115) in V1
---
 doc/README.SPL                     |    1 +
 drivers/mtd/nand/Makefile          |   10 +-
 drivers/mtd/nand/nand_base.c       |    4 +-
 drivers/mtd/nand/nand_spl_simple.c |  257 ++++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/omap_gpmc.c       |   27 ++++
 include/nand.h                     |    6 +-
 spl/Makefile                       |    1 +
 7 files changed, 301 insertions(+), 5 deletions(-)
 create mode 100644 drivers/mtd/nand/nand_spl_simple.c

Comments

Scott Wood Aug. 1, 2011, 5:58 p.m. UTC | #1
On Fri, 29 Jul 2011 18:53:38 +0200
Simon Schwarz <simonschwarzcor@googlemail.com> wrote:

> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index 8b598f6..fcd8b74 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -26,12 +26,18 @@ include $(TOPDIR)/config.mk
>  LIB	:= $(obj)libnand.o
>  
>  ifdef CONFIG_CMD_NAND
> +ifdef CONFIG_SPL_BUILD
> +ifdef CONFIG_OMAP34XX
> +COBJS-y += nand_spl_simple.o
> +endif
> +else

Please define a symbol for nand_spl_simple, and have the platform config
file select it.

> +int nand_spl_load_image(loff_t offs, unsigned int size, void *dst)
> +{
> +	unsigned int block, lastblock;
> +	unsigned int page;
> +
> +	/*
> +	 * offs has to be aligned to a page address!
> +	 */
> +	block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
> +	lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE;
> +	page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;

Hmm, maybe it's better to leave offs as 32-bit, if we're going to be
dividing.  The existing SPL has it as "unsigned int", and it's unlikely
that the SPL will be loading from offsets above 4GiB.  Sorry about the back
and forth...

> +/* SPL interface to read a page */
> +void nand_spl_read_page(loff_t offs, void *dst)
> +{
> +	int block, page;
> +	/* calc the block */
> +	block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
> +	/* calc the page */
> +	page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
> +	/* read */
> +	nand_read_page(block, page, dst);
> +}

How does this differ from nand_spl_load_image() with size ==
CONFIG_SYS_NAND_PAGE_SIZE, other than the lack of bad block skipping?

-Scott
Simon Schwarz Aug. 2, 2011, 8:15 a.m. UTC | #2
On 08/01/2011 07:58 PM, Scott Wood wrote:
> On Fri, 29 Jul 2011 18:53:38 +0200
> Simon Schwarz<simonschwarzcor@googlemail.com>  wrote:
>
>> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
>> index 8b598f6..fcd8b74 100644
>> --- a/drivers/mtd/nand/Makefile
>> +++ b/drivers/mtd/nand/Makefile
>> @@ -26,12 +26,18 @@ include $(TOPDIR)/config.mk
>>   LIB	:= $(obj)libnand.o
>>
>>   ifdef CONFIG_CMD_NAND
>> +ifdef CONFIG_SPL_BUILD
>> +ifdef CONFIG_OMAP34XX
>> +COBJS-y += nand_spl_simple.o
>> +endif
>> +else
>
> Please define a symbol for nand_spl_simple, and have the platform config
> file select it.
done.
>
>> +int nand_spl_load_image(loff_t offs, unsigned int size, void *dst)
>> +{
>> +	unsigned int block, lastblock;
>> +	unsigned int page;
>> +
>> +	/*
>> +	 * offs has to be aligned to a page address!
>> +	 */
>> +	block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
>> +	lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE;
>> +	page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
>
> Hmm, maybe it's better to leave offs as 32-bit, if we're going to be
> dividing.  The existing SPL has it as "unsigned int", and it's unlikely
> that the SPL will be loading from offsets above 4GiB.  Sorry about the back
> and forth...
hm ok.

>
>> +/* SPL interface to read a page */
>> +void nand_spl_read_page(loff_t offs, void *dst)
>> +{
>> +	int block, page;
>> +	/* calc the block */
>> +	block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
>> +	/* calc the page */
>> +	page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
>> +	/* read */
>> +	nand_read_page(block, page, dst);
>> +}
>
> How does this differ from nand_spl_load_image() with size ==
> CONFIG_SYS_NAND_PAGE_SIZE, other than the lack of bad block skipping?

Damn bad block skipping. right. Will change.

> -Scott
>

Regards & thanks for review!
diff mbox

Patch

diff --git a/doc/README.SPL b/doc/README.SPL
index ce8e19f..ef946ce 100644
--- a/doc/README.SPL
+++ b/doc/README.SPL
@@ -60,3 +60,4 @@  CONFIG_SPL_SPI_FLASH_SUPPORT (drivers/mtd/spi/libspi_flash.o)
 CONFIG_SPL_SPI_SUPPORT (drivers/spi/libspi.o)
 CONFIG_SPL_FAT_SUPPORT (fs/fat/libfat.o)
 CONFIG_SPL_LIBGENERIC_SUPPORT (lib/libgeneric.o)
+CONFIG_SPL_NAND_SUPPORT (drivers/mtd/nand/libnand.o)
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 8b598f6..fcd8b74 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -26,12 +26,18 @@  include $(TOPDIR)/config.mk
 LIB	:= $(obj)libnand.o
 
 ifdef CONFIG_CMD_NAND
+ifdef CONFIG_SPL_BUILD
+ifdef CONFIG_OMAP34XX
+COBJS-y += nand_spl_simple.o
+endif
+else
 COBJS-y += nand.o
-COBJS-y += nand_base.o
 COBJS-y += nand_bbt.o
-COBJS-y += nand_ecc.o
 COBJS-y += nand_ids.o
 COBJS-y += nand_util.o
+endif
+COBJS-y += nand_ecc.o
+COBJS-y += nand_base.o
 
 COBJS-$(CONFIG_NAND_ATMEL) += atmel_nand.o
 COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 1a95a91..e7dfcb1 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -213,7 +213,7 @@  static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
  *
  * Default read function for 8bit buswith
  */
-static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
 	int i;
 	struct nand_chip *chip = mtd->priv;
@@ -269,7 +269,7 @@  static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
  *
  * Default read function for 16bit buswith
  */
-static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
 {
 	int i;
 	struct nand_chip *chip = mtd->priv;
diff --git a/drivers/mtd/nand/nand_spl_simple.c b/drivers/mtd/nand/nand_spl_simple.c
new file mode 100644
index 0000000..e33085e
--- /dev/null
+++ b/drivers/mtd/nand/nand_spl_simple.c
@@ -0,0 +1,257 @@ 
+/*
+ * (C) Copyright 2006-2008
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/io.h>
+
+static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
+static nand_info_t mtd;
+static struct nand_chip nand_chip;
+
+#if (CONFIG_SYS_NAND_PAGE_SIZE <= 512)
+/*
+ * NAND command for small page NAND devices (512)
+ */
+static int nand_command(int block, int page, loff_t offs,
+	u8 cmd)
+{
+	struct nand_chip *this = mtd.priv;
+	int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+
+	while (!this->dev_ready(&mtd))
+		;
+
+	/* Begin command latch cycle */
+	this->cmd_ctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+	/* Set ALE and clear CLE to start address cycle */
+	/* Column address */
+	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,
+		       NAND_CTRL_ALE); /* A[24:17] */
+#ifdef CONFIG_SYS_NAND_4_ADDR_CYCLE
+	/* One more address cycle for devices > 32MiB */
+	this->cmd_ctrl(&mtd, (page_addr >> 16) & 0x0f,
+		       NAND_CTRL_ALE); /* A[28:25] */
+#endif
+	/* Latch in address */
+	this->cmd_ctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+	/*
+	 * Wait a while for the data to be ready
+	 */
+	while (!this->dev_ready(&mtd))
+		;
+
+	return 0;
+}
+#else
+/*
+ * NAND command for large page NAND devices (2k)
+ */
+static int nand_command(int block, int page, loff_t offs,
+	u8 cmd)
+{
+	struct nand_chip *this = mtd.priv;
+	int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+	void (*hwctrl)(struct mtd_info *mtd, int cmd,
+			unsigned int ctrl) = this->cmd_ctrl;
+
+	while (!this->dev_ready(&mtd))
+		;
+
+	/* Emulate NAND_CMD_READOOB */
+	if (cmd == NAND_CMD_READOOB) {
+		offs += CONFIG_SYS_NAND_PAGE_SIZE;
+		cmd = NAND_CMD_READ0;
+	}
+
+	/* Shift the offset from byte addressing to word addressing. */
+	if (this->options & NAND_BUSWIDTH_16)
+		offs >>= 1;
+
+	/* Begin command latch cycle */
+	hwctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+	/* Set ALE and clear CLE to start address cycle */
+	/* Column address */
+	hwctrl(&mtd, offs & 0xff,
+		       NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */
+	hwctrl(&mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */
+	/* Row address */
+	hwctrl(&mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */
+	hwctrl(&mtd, ((page_addr >> 8) & 0xff),
+		       NAND_CTRL_ALE); /* A[27:20] */
+#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
+	/* One more address cycle for devices > 128MiB */
+	hwctrl(&mtd, (page_addr >> 16) & 0x0f,
+		       NAND_CTRL_ALE); /* A[31:28] */
+#endif
+	/* Latch in address */
+	hwctrl(&mtd, NAND_CMD_READSTART,
+		       NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+	hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+	/*
+	 * Wait a while for the data to be ready
+	 */
+	while (!this->dev_ready(&mtd))
+		;
+
+	return 0;
+}
+#endif
+
+static int nand_is_bad_block(int block)
+{
+	struct nand_chip *this = mtd.priv;
+
+	nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS,
+		NAND_CMD_READOOB);
+
+	/*
+	 * Read one byte (or two if it's a 16 bit chip).
+	 */
+	if (this->options & NAND_BUSWIDTH_16) {
+		if (readw(this->IO_ADDR_R) != 0xffff)
+			return 1;
+	} else {
+		if (readb(this->IO_ADDR_R) != 0xff)
+			return 1;
+	}
+
+	return 0;
+}
+
+static int nand_read_page(int block, int page, void *dst)
+{
+	struct nand_chip *this = mtd.priv;
+	u_char *ecc_calc;
+	u_char *ecc_code;
+	u_char *oob_data;
+	int i;
+	int eccsize = CONFIG_SYS_NAND_ECCSIZE;
+	int eccbytes = CONFIG_SYS_NAND_ECCBYTES;
+	int eccsteps = CONFIG_SYS_NAND_ECCSTEPS;
+	uint8_t *p = dst;
+	int stat;
+
+	nand_command(block, page, 0, NAND_CMD_READ0);
+
+	/* No malloc available for now, just use some temporary locations
+	 * in SDRAM
+	 */
+	ecc_calc = (u_char *)(CONFIG_SYS_SDRAM_BASE + 0x10000);
+	ecc_code = ecc_calc + 0x100;
+	oob_data = ecc_calc + 0x200;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		this->ecc.hwctl(&mtd, NAND_ECC_READ);
+		this->read_buf(&mtd, p, eccsize);
+		this->ecc.calculate(&mtd, p, &ecc_calc[i]);
+	}
+	this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE);
+
+	/* Pick the ECC bytes out of the oob data */
+	for (i = 0; i < CONFIG_SYS_NAND_ECCTOTAL; i++)
+		ecc_code[i] = oob_data[nand_ecc_pos[i]];
+
+	eccsteps = CONFIG_SYS_NAND_ECCSTEPS;
+	p = dst;
+
+	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		/* No chance to do something with the possible error message
+		 * from correct_data(). We just hope that all possible errors
+		 * are corrected by this routine.
+		 */
+		stat = this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]);
+	}
+
+	return 0;
+}
+
+int nand_spl_load_image(loff_t offs, unsigned int size, void *dst)
+{
+	unsigned int block, lastblock;
+	unsigned int page;
+
+	/*
+	 * offs has to be aligned to a page address!
+	 */
+	block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
+	lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE;
+	page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
+
+	while (block <= lastblock) {
+		if (!nand_is_bad_block(block)) {
+			/*
+			 * Skip bad blocks
+			 */
+			while (page < CONFIG_SYS_NAND_PAGE_COUNT) {
+				nand_read_page(block, page, dst);
+				dst += CONFIG_SYS_NAND_PAGE_SIZE;
+				page++;
+			}
+
+			page = 0;
+		} else {
+			lastblock++;
+		}
+
+		block++;
+	}
+
+	return 0;
+}
+
+/* nand_init() - initialize data to make nand usable by SPL */
+void nand_init(void)
+{
+	/*
+	 * Init board specific nand support
+	 */
+	mtd.priv = &nand_chip;
+	nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W =
+		(void  __iomem *)CONFIG_SYS_NAND_BASE;
+	nand_chip.options = 0;
+	board_nand_init(&nand_chip);
+
+	if (nand_chip.select_chip)
+		nand_chip.select_chip(&mtd, 0);
+}
+
+/* SPL interface to read a page */
+void nand_spl_read_page(loff_t offs, void *dst)
+{
+	int block, page;
+	/* calc the block */
+	block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
+	/* calc the page */
+	page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
+	/* read */
+	nand_read_page(block, page, dst);
+}
+
+/* Unselect after operation */
+void nand_deselect(void)
+{
+	if (nand_chip.select_chip)
+		nand_chip.select_chip(&mtd, -1);
+}
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
index 99b9cef..5bbec48 100644
--- a/drivers/mtd/nand/omap_gpmc.c
+++ b/drivers/mtd/nand/omap_gpmc.c
@@ -61,6 +61,14 @@  static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd,
 		writeb(cmd, this->IO_ADDR_W);
 }
 
+#ifdef CONFIG_SPL_BUILD
+/* Check wait pin as dev ready indicator */
+int omap_spl_dev_ready(struct mtd_info *mtd)
+{
+	return gpmc_cfg->status & (1 << 8);
+}
+#endif
+
 /*
  * omap_hwecc_init - Initialize the Hardware ECC for NAND flash in
  *                   GPMC controller
@@ -224,6 +232,7 @@  static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
 	}
 }
 
+#ifndef CONFIG_SPL_BUILD
 /*
  * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc.
  * The default is to come up on s/w ecc
@@ -280,6 +289,7 @@  void omap_nand_switch_ecc(int32_t hardware)
 
 	nand->options &= ~NAND_OWN_BUFFERS;
 }
+#endif /* CONFIG_SPL_BUILD */
 
 /*
  * Board-specific NAND initialization. The following members of the
@@ -338,7 +348,24 @@  int board_nand_init(struct nand_chip *nand)
 
 	nand->chip_delay = 100;
 	/* Default ECC mode */
+#ifndef CONFIG_SPL_BUILD
 	nand->ecc.mode = NAND_ECC_SOFT;
+#else
+	nand->ecc.mode = NAND_ECC_HW;
+	nand->ecc.layout = &hw_nand_oob;
+	nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
+	nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
+	nand->ecc.hwctl = omap_enable_hwecc;
+	nand->ecc.correct = omap_correct_data;
+	nand->ecc.calculate = omap_calculate_ecc;
+	omap_hwecc_init(nand);
+
+	if (nand->options & NAND_BUSWIDTH_16)
+		nand->read_buf = nand_read_buf16;
+	else
+		nand->read_buf = nand_read_buf;
+	nand->dev_ready = omap_spl_dev_ready;
+#endif
 
 	return 0;
 }
diff --git a/include/nand.h b/include/nand.h
index 3c5ef4e..54905bb 100644
--- a/include/nand.h
+++ b/include/nand.h
@@ -132,9 +132,13 @@  int nand_lock( nand_info_t *meminfo, int tight );
 int nand_unlock( nand_info_t *meminfo, ulong start, ulong length );
 int nand_get_lock_status(nand_info_t *meminfo, loff_t offset);
 
-void nand_spl_load_image(loff_t offs, unsigned int size, uchar *dst);
+int nand_spl_load_image(loff_t offs, unsigned int size, void *dst);
+void nand_spl_read_page(loff_t offs, void *dst);
 void nand_deselect(void);
 
+void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len);
+void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len);
+
 #ifdef CONFIG_SYS_NAND_SELECT_DEVICE
 void board_nand_select_device(struct nand_chip *nand, int chip);
 #endif
diff --git a/spl/Makefile b/spl/Makefile
index 87f13f6..17d4f7f 100644
--- a/spl/Makefile
+++ b/spl/Makefile
@@ -46,6 +46,7 @@  LIBS-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += drivers/mtd/spi/libspi_flash.o
 LIBS-$(CONFIG_SPL_SPI_SUPPORT) += drivers/spi/libspi.o
 LIBS-$(CONFIG_SPL_FAT_SUPPORT) += fs/fat/libfat.o
 LIBS-$(CONFIG_SPL_LIBGENERIC_SUPPORT) += lib/libgeneric.o
+LIBS-$(CONFIG_SPL_NAND_SUPPORT) += drivers/mtd/nand/libnand.o
 
 ifeq ($(SOC),omap3)
 LIBS-y += $(CPUDIR)/omap-common/libomap-common.o