[U-Boot] [PATCH 5/6] tegra: nand: Add Tegra NAND driver

Simon Glass sjg at chromium.org
Sat Jan 14 00:10:55 CET 2012


From: Jim Lin <jilin at nvidia.com>

A device tree is used to configure the NAND, including memory
timings and block/pages sizes.

If this node is not present or is disabled, then NAND will not
be initialized.

Signed-off-by: Simon Glass <sjg at chromium.org>
---
 arch/arm/include/asm/arch-tegra2/tegra2.h |    1 +
 drivers/mtd/nand/Makefile                 |    1 +
 drivers/mtd/nand/tegra2_nand.c            | 1074 +++++++++++++++++++++++++++++
 drivers/mtd/nand/tegra2_nand.h            |  303 ++++++++
 include/fdtdec.h                          |    1 +
 lib/fdtdec.c                              |    1 +
 6 files changed, 1381 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/tegra2_nand.c
 create mode 100644 drivers/mtd/nand/tegra2_nand.h

diff --git a/arch/arm/include/asm/arch-tegra2/tegra2.h b/arch/arm/include/asm/arch-tegra2/tegra2.h
index d4ada10..a080b63 100644
--- a/arch/arm/include/asm/arch-tegra2/tegra2.h
+++ b/arch/arm/include/asm/arch-tegra2/tegra2.h
@@ -39,6 +39,7 @@
 #define NV_PA_APB_UARTC_BASE	(NV_PA_APB_MISC_BASE + 0x6200)
 #define NV_PA_APB_UARTD_BASE	(NV_PA_APB_MISC_BASE + 0x6300)
 #define NV_PA_APB_UARTE_BASE	(NV_PA_APB_MISC_BASE + 0x6400)
+#define TEGRA2_NAND_BASE	(NV_PA_APB_MISC_BASE + 0x8000)
 #define TEGRA2_SPI_BASE		(NV_PA_APB_MISC_BASE + 0xC380)
 #define TEGRA2_PMC_BASE		(NV_PA_APB_MISC_BASE + 0xE400)
 #define TEGRA2_FUSE_BASE	(NV_PA_APB_MISC_BASE + 0xF800)
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 36ee454..5bb67e8 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -60,6 +60,7 @@ COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o
 COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
 COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
 COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o
+COBJS-$(CONFIG_TEGRA2_NAND) += tegra2_nand.o
 COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
 COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o
 endif
diff --git a/drivers/mtd/nand/tegra2_nand.c b/drivers/mtd/nand/tegra2_nand.c
new file mode 100644
index 0000000..e4ae6fc
--- /dev/null
+++ b/drivers/mtd/nand/tegra2_nand.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
+ * (C) Copyright 2006 Detlev Zundel, dzu at denx.de
+ * (C) Copyright 2006 DENX Software Engineering
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <asm/io.h>
+#include <nand.h>
+#include <asm/arch/clk_rst.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/funcmux.h>
+#include <asm/arch/gpio.h>
+#include <asm/errno.h>
+#include <asm-generic/gpio.h>
+#include <fdtdec.h>
+#include "tegra2_nand.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NAND_CMD_TIMEOUT_MS		10
+
+static struct nand_ecclayout eccoob = {
+	.eccpos = {
+		4,  5,  6,  7,  8,  9,  10, 11, 12,
+		13, 14, 15, 16, 17, 18, 19, 20, 21,
+		22, 23, 24, 25, 26, 27, 28, 29, 30,
+		31, 32, 33, 34, 35, 36, 37, 38, 39,
+		60, 61, 62, 63,
+	},
+};
+
+enum {
+	ECC_OK,
+	ECC_TAG_ERROR = 1 << 0,
+	ECC_DATA_ERROR = 1 << 1
+};
+
+/* Timing parameters */
+enum {
+	FDT_NAND_MAX_TRP_TREA,
+	FDT_NAND_TWB,
+	FDT_NAND_MAX_TCR_TAR_TRR,
+	FDT_NAND_TWHR,
+	FDT_NAND_MAX_TCS_TCH_TALS_TALH,
+	FDT_NAND_TWH,
+	FDT_NAND_TWP,
+	FDT_NAND_TRH,
+	FDT_NAND_TADL,
+
+	FDT_NAND_TIMING_COUNT
+};
+
+/* Information about an attached NAND chip */
+struct fdt_nand {
+	struct nand_ctlr *reg;
+	int enabled;		/* 1 to enable, 0 to disable */
+	struct fdt_gpio_state wp_gpio;	/* write-protect GPIO */
+	int width;		/* bit width, normally 8 */
+	int tag_ecc_bytes;	/* ECC bytes to be generated for tag bytes */
+	int tag_bytes;		/* Tag bytes in spare area */
+	int data_ecc_bytes;	/* ECC bytes for data area */
+	int skipped_spare_bytes;
+	/*
+	 * How many bytes in spare area:
+	 * spare area = skipped bytes + ECC bytes of data area
+	 * + tag bytes + ECC bytes of tag bytes
+	 */
+	int page_spare_bytes;
+	int page_data_bytes;	/* Bytes in data area */
+	unsigned timing[FDT_NAND_TIMING_COUNT];
+};
+
+struct nand_info {
+	struct nand_ctlr *reg;
+
+	/*
+	* When running in PIO mode to get READ ID bytes from register
+	* RESP_0, we need this variable as an index to know which byte in
+	* register RESP_0 should be read.
+	* Because common code in nand_base.c invokes read_byte function two
+	* times for NAND_CMD_READID.
+	* And our controller returns 4 bytes at once in register RESP_0.
+	*/
+	int pio_byte_index;
+	struct fdt_nand config;
+};
+
+struct nand_info nand_ctrl;
+
+/**
+ * Wait for command completion
+ *
+ * @param reg	nand_ctlr structure
+ * @return
+ *	1 - Command completed
+ *	0 - Timeout
+ */
+static int nand_waitfor_cmd_completion(struct nand_ctlr *reg)
+{
+	int i;
+	u32 reg_val;
+
+	for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) {
+		if ((readl(&reg->command) & CMD_GO) ||
+			!(readl(&reg->status) &
+				STATUS_RBSY0) ||
+			!(readl(&reg->isr) &
+				ISR_IS_CMD_DONE)) {
+			udelay(1);
+			continue;
+		}
+		reg_val = readl(&reg->dma_mst_ctrl);
+		/*
+		 * If DMA_MST_CTRL_EN_A_ENABLE or
+		 * DMA_MST_CTRL_EN_B_ENABLE is set,
+		 * that means DMA engine is running, then we
+		 * have to wait until
+		 * DMA_MST_CTRL_IS_DMA_DONE
+		 * is cleared for DMA transfer completion.
+		 */
+		if (reg_val & (DMA_MST_CTRL_EN_A_ENABLE |
+			DMA_MST_CTRL_EN_B_ENABLE)) {
+			if (reg_val & DMA_MST_CTRL_IS_DMA_DONE)
+				return 1;
+		} else
+			return 1;
+		udelay(1);
+	}
+	return 0;
+}
+
+/**
+ * [DEFAULT] Read one byte from the chip
+ *
+ * @param mtd	MTD device structure
+ * @return	data byte
+ *
+ * Default read function for 8bit bus-width
+ */
+static uint8_t read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	int dword_read;
+	struct nand_info *info;
+
+	info = (struct nand_info *) chip->priv;
+
+	dword_read = readl(&info->reg->resp);
+	dword_read = dword_read >> (8 * info->pio_byte_index);
+	info->pio_byte_index++;
+	return (uint8_t) dword_read;
+}
+
+/**
+ * [DEFAULT] Write buffer to chip
+ *
+ * @param mtd	MTD device structure
+ * @param buf	data buffer
+ * @param len	number of bytes to write
+ *
+ * Default write function for 8bit bus-width
+ */
+static void write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	int i, j, l;
+	struct nand_chip *chip = mtd->priv;
+	struct nand_info *info;
+
+	info = (struct nand_info *) chip->priv;
+
+	for (i = 0; i < len / 4; i++) {
+		l = ((int *)buf)[i];
+		writel(l, &info->reg->resp);
+		writel(CMD_GO | CMD_PIO | CMD_TX |
+			(CMD_TRANS_SIZE_BYTES4 <<
+				CMD_TRANS_SIZE_SHIFT)
+			| CMD_A_VALID | CMD_CE0,
+			&info->reg->command);
+
+		if (!nand_waitfor_cmd_completion(info->reg))
+			printf("Command timeout during write_buf\n");
+	}
+	if (len & 3) {
+		l = 0;
+		for (j = 0; j < (len & 3); j++)
+			l |= (((int) buf[i * 4 + j]) << (8 * j));
+
+		writel(l, &info->reg->resp);
+		writel(CMD_GO | CMD_PIO | CMD_TX |
+			(((len & 3) - 1) << CMD_TRANS_SIZE_SHIFT) |
+			CMD_A_VALID | CMD_CE0,
+			&info->reg->command);
+		if (!nand_waitfor_cmd_completion(info->reg))
+			printf("Command timeout during write_buf\n");
+	}
+}
+
+/**
+ * [DEFAULT] Read chip data into buffer
+ *
+ * @param mtd	MTD device structure
+ * @param buf	buffer to store date
+ * @param len	number of bytes to read
+ *
+ * Default read function for 8bit bus-width
+ */
+static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	int i, j, l;
+	struct nand_chip *chip = mtd->priv;
+	int *buf_dword;
+	struct nand_info *info;
+
+	info = (struct nand_info *) chip->priv;
+
+	buf_dword = (int *) buf;
+	for (i = 0; i < len / 4; i++) {
+		writel(CMD_GO | CMD_PIO | CMD_RX |
+			(CMD_TRANS_SIZE_BYTES4 <<
+				CMD_TRANS_SIZE_SHIFT)
+			| CMD_A_VALID | CMD_CE0,
+			&info->reg->command);
+		if (!nand_waitfor_cmd_completion(info->reg))
+			printf("Command timeout during read_buf\n");
+		l = readl(&info->reg->resp);
+		buf_dword[i] = l;
+	}
+	if (len & 3) {
+		writel(CMD_GO | CMD_PIO | CMD_RX |
+			(((len & 3) - 1) << CMD_TRANS_SIZE_SHIFT) |
+			CMD_A_VALID | CMD_CE0,
+			&info->reg->command);
+		if (!nand_waitfor_cmd_completion(info->reg))
+			printf("Command timeout during read_buf\n");
+		l = readl(&info->reg->resp);
+		for (j = 0; j < (len & 3); j++)
+			buf[i * 4 + j] = (char) (l >> (8 * j));
+	}
+}
+
+/**
+ * Check NAND status to see if it is ready or not
+ *
+ * @param mtd	MTD device structure
+ * @return
+ *	1 - ready
+ *	0 - not ready
+ */
+static int nand_dev_ready(struct mtd_info *mtd)
+{
+	register struct nand_chip *chip = mtd->priv;
+	int reg_val;
+	struct nand_info *info;
+
+	info = (struct nand_info *) chip->priv;
+
+	reg_val = readl(&info->reg->status);
+	if (reg_val & STATUS_RBSY0)
+		return 1;
+	else
+		return 0;
+}
+
+/* Hardware specific access to control-lines */
+static void nand_hwcontrol(struct mtd_info *mtd, int cmd,
+	unsigned int ctrl)
+{
+}
+
+/**
+ * Clear all interrupt status bits
+ *
+ * @param reg	nand_ctlr structure
+ */
+static void nand_clear_interrupt_status(struct nand_ctlr *reg)
+{
+	u32 reg_val;
+
+	/* Clear interrupt status */
+	reg_val = readl(&reg->isr);
+	writel(reg_val, &reg->isr);
+}
+
+/**
+ * [DEFAULT] Send command to NAND device
+ *
+ * @param mtd		MTD device structure
+ * @param command	the command to be sent
+ * @param column	the column address for this command, -1 if none
+ * @param page_addr	the page address for this command, -1 if none
+ */
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+	int column, int page_addr)
+{
+	register struct nand_chip *chip = mtd->priv;
+	struct nand_info *info;
+
+	info = (struct nand_info *) chip->priv;
+
+	/*
+	 * Write out the command to the device.
+	 */
+	if (mtd->writesize < 2048) {
+		/*
+		 * Only command NAND_CMD_RESET or NAND_CMD_READID will come
+		 * here before mtd->writesize is initialized, we don't have
+		 * any action here because page size of NAND HY27UF084G2B
+		 * is 2048 bytes and mtd->writesize will be 2048 after
+		 * initialized.
+		 */
+	} else {
+		/* Emulate NAND_CMD_READOOB */
+		if (command == NAND_CMD_READOOB) {
+			column += mtd->writesize;
+			command = NAND_CMD_READ0;
+		}
+
+		/* Adjust columns for 16 bit bus-width */
+		if (column != -1 && (chip->options & NAND_BUSWIDTH_16))
+			column >>= 1;
+	}
+
+	nand_clear_interrupt_status(info->reg);
+
+	/* Stop DMA engine, clear DMA completion status */
+	writel(DMA_MST_CTRL_EN_A_DISABLE
+		| DMA_MST_CTRL_EN_B_DISABLE
+		| DMA_MST_CTRL_IS_DMA_DONE,
+		&info->reg->dma_mst_ctrl);
+
+	/*
+	 * Program and erase have their own busy handlers
+	 * status and sequential in needs no delay
+	 */
+	switch (command) {
+	case NAND_CMD_READID:
+		writel(NAND_CMD_READID, &info->reg->cmd_reg1);
+		writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_PIO
+			| CMD_RX |
+			(CMD_TRANS_SIZE_BYTES4 << CMD_TRANS_SIZE_SHIFT)
+			| CMD_CE0,
+			&info->reg->command);
+		info->pio_byte_index = 0;
+		break;
+	case NAND_CMD_READ0:
+		writel(NAND_CMD_READ0, &info->reg->cmd_reg1);
+		writel(NAND_CMD_READSTART, &info->reg->cmd_reg2);
+		writel((page_addr << 16) | (column & 0xFFFF),
+			&info->reg->addr_reg1);
+		writel(page_addr >> 16, &info->reg->addr_reg2);
+		return;
+	case NAND_CMD_SEQIN:
+		writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1);
+		writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2);
+		writel((page_addr << 16) | (column & 0xFFFF),
+			&info->reg->addr_reg1);
+		writel(page_addr >> 16,
+			&info->reg->addr_reg2);
+		return;
+	case NAND_CMD_PAGEPROG:
+		return;
+	case NAND_CMD_ERASE1:
+		writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1);
+		writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2);
+		writel(page_addr, &info->reg->addr_reg1);
+		writel(CMD_GO | CMD_CLE | CMD_ALE |
+			CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3,
+			&info->reg->command);
+		break;
+	case NAND_CMD_RNDOUT:
+		return;
+	case NAND_CMD_ERASE2:
+		return;
+	case NAND_CMD_STATUS:
+		writel(NAND_CMD_STATUS, &info->reg->cmd_reg1);
+		writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX
+			| (CMD_TRANS_SIZE_BYTES1 <<
+				CMD_TRANS_SIZE_SHIFT)
+			| CMD_CE0,
+			&info->reg->command);
+		info->pio_byte_index = 0;
+		break;
+	case NAND_CMD_RESET:
+		writel(NAND_CMD_RESET, &info->reg->cmd_reg1);
+		writel(CMD_GO | CMD_CLE | CMD_CE0,
+			&info->reg->command);
+		break;
+	default:
+		return;
+	}
+	if (!nand_waitfor_cmd_completion(info->reg))
+		printf("Command 0x%02X timeout\n", command);
+}
+
+/**
+ * Check whether the pointed buffer are all 0xff (blank).
+ *
+ * @param buf	data buffer for blank check
+ * @param len	length of the buffer in byte
+ * @return
+ *	1 - blank
+ *	0 - non-blank
+ */
+static int blank_check(u8 *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		if (buf[i] != 0xFF)
+			return 0;
+	return 1;
+}
+
+/**
+ * After a DMA transfer for read, we call this function to see whether there
+ * is any uncorrectable error on the pointed data buffer or oob buffer.
+ *
+ * @param reg		nand_ctlr structure
+ * @param databuf	data buffer
+ * @param a_len		data buffer length
+ * @param oobbuf	oob buffer
+ * @param b_len		oob buffer length
+ * @return
+ *	ECC_OK - no ECC error or correctable ECC error
+ *	ECC_TAG_ERROR - uncorrectable tag ECC error
+ *	ECC_DATA_ERROR - uncorrectable data ECC error
+ *	ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error
+ */
+static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf,
+	int a_len, u8 *oobbuf, int b_len)
+{
+	int return_val = ECC_OK;
+	u32 reg_val;
+
+	if (!(readl(&reg->isr) & ISR_IS_ECC_ERR))
+		return ECC_OK;
+
+	reg_val = readl(&reg->dec_status);
+	if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) {
+		reg_val = readl(&reg->bch_dec_status_buf);
+		/*
+		 * If uncorrectable error occurs on data area, then see whether
+		 * they are all FF. If all are FF, it's a blank page.
+		 * Not error.
+		 */
+		if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) &&
+			!blank_check(databuf, a_len))
+			return_val |= ECC_DATA_ERROR;
+	}
+
+	if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) {
+		reg_val = readl(&reg->bch_dec_status_buf);
+		/*
+		 * If uncorrectable error occurs on tag area, then see whether
+		 * they are all FF. If all are FF, it's a blank page.
+		 * Not error.
+		 */
+		if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) &&
+			!blank_check(oobbuf, b_len))
+			return_val |= ECC_TAG_ERROR;
+	}
+
+	return return_val;
+}
+
+/**
+ * Set GO bit to send command to device
+ *
+ * @param reg	nand_ctlr structure
+ */
+static void start_command(struct nand_ctlr *reg)
+{
+	u32 reg_val;
+
+	reg_val = readl(&reg->command);
+	reg_val |= CMD_GO;
+	writel(reg_val, &reg->command);
+}
+
+/**
+ * Clear command GO bit, DMA GO bit, and DMA completion status
+ *
+ * @param reg	nand_ctlr structure
+ */
+static void stop_command(struct nand_ctlr *reg)
+{
+	/* Stop command */
+	writel(0, &reg->command);
+
+	/* Stop DMA engine and clear DMA completion status */
+	writel(DMA_MST_CTRL_GO_DISABLE
+		| DMA_MST_CTRL_IS_DMA_DONE,
+		&reg->dma_mst_ctrl);
+}
+
+/**
+ * Set up NAND bus width and page size
+ *
+ * @param info		nand_info structure
+ * @param *reg_val	address of reg_val
+ * @return none - value is set in reg_val
+ */
+static void set_bus_width_page_size(struct fdt_nand *config,
+	u32 *reg_val)
+{
+	if (config->width == 8)
+		*reg_val = CFG_BUS_WIDTH_8BIT;
+	else
+		*reg_val = CFG_BUS_WIDTH_16BIT;
+
+	if (config->page_data_bytes == 256)
+		*reg_val |= CFG_PAGE_SIZE_256;
+	else if (config->page_data_bytes == 512)
+		*reg_val |= CFG_PAGE_SIZE_512;
+	else if (config->page_data_bytes == 1024)
+		*reg_val |= CFG_PAGE_SIZE_1024;
+	else if (config->page_data_bytes == 2048)
+		*reg_val |= CFG_PAGE_SIZE_2048;
+}
+
+/**
+ * Page read/write function
+ *
+ * @param mtd		mtd info structure
+ * @param chip		nand chip info structure
+ * @param buf		data buffer
+ * @param page		page number
+ * @param with_ecc	1 to enable ECC, 0 to disable ECC
+ * @param is_writing	0 for read, 1 for write
+ * @return	0 when successfully completed
+ *		-EIO when command timeout
+ */
+static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
+	uint8_t *buf, int page, int with_ecc, int is_writing)
+{
+	u32 reg_val;
+	int tag_size;
+	struct nand_oobfree *free = chip->ecc.layout->oobfree;
+	/* 128 is larger than the value that our HW can support. */
+	u32 tag_buf[128];
+	char *tag_ptr;
+	struct nand_info *info;
+	struct fdt_nand *config;
+
+	if (((int) buf) & 0x03) {
+		printf("buf 0x%X has to be 4-byte aligned\n", (u32) buf);
+		return -EINVAL;
+	}
+
+	info = (struct nand_info *) chip->priv;
+	config = &info->config;
+
+	/* Need to be 4-byte aligned */
+	tag_ptr = (char *) &tag_buf;
+
+	stop_command(info->reg);
+
+	writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a);
+	writel((u32) buf, &info->reg->data_block_ptr);
+
+	if (with_ecc) {
+		writel((u32) tag_ptr, &info->reg->tag_ptr);
+		if (is_writing)
+			memcpy(tag_ptr, chip->oob_poi + free->offset,
+				config->tag_bytes +
+				config->tag_ecc_bytes);
+	} else
+		writel((u32) chip->oob_poi, &info->reg->tag_ptr);
+
+	set_bus_width_page_size(&info->config, &reg_val);
+
+	/* Set ECC selection, configure ECC settings */
+	if (with_ecc) {
+		tag_size = config->tag_bytes + config->tag_ecc_bytes;
+		reg_val |= (CFG_SKIP_SPARE_SEL_4
+			| CFG_SKIP_SPARE_ENABLE
+			| CFG_HW_ECC_CORRECTION_ENABLE
+			| CFG_ECC_EN_TAG_DISABLE
+			| CFG_HW_ECC_SEL_RS
+			| CFG_HW_ECC_ENABLE
+			| CFG_TVAL4
+			| (tag_size - 1));
+
+		if (!is_writing) {
+			tag_size += config->skipped_spare_bytes;
+			invalidate_dcache_range((unsigned long) tag_ptr,
+				((unsigned long) tag_ptr) + tag_size);
+		} else
+			flush_dcache_range((unsigned long) tag_ptr,
+				((unsigned long) tag_ptr) + tag_size);
+	} else {
+		tag_size = mtd->oobsize;
+		reg_val |= (CFG_SKIP_SPARE_DISABLE
+			| CFG_HW_ECC_CORRECTION_DISABLE
+			| CFG_ECC_EN_TAG_DISABLE
+			| CFG_HW_ECC_DISABLE
+			| (tag_size - 1));
+		if (!is_writing) {
+			invalidate_dcache_range((unsigned long) chip->oob_poi,
+				((unsigned long) chip->oob_poi) + tag_size);
+		} else {
+			flush_dcache_range((unsigned long) chip->oob_poi,
+				((unsigned long) chip->oob_poi) + tag_size);
+		}
+	}
+	writel(reg_val, &info->reg->config);
+
+	if (!is_writing) {
+		invalidate_dcache_range((unsigned long) buf,
+			((unsigned long) buf) +
+			(1 << chip->page_shift));
+	} else {
+		flush_dcache_range((unsigned long) buf,
+			((unsigned long) buf) +
+			(1 << chip->page_shift));
+	}
+
+	writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
+	writel(tag_size - 1, &info->reg->dma_cfg_b);
+
+	nand_clear_interrupt_status(info->reg);
+
+	reg_val = CMD_CLE | CMD_ALE
+		| CMD_SEC_CMD
+		| (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
+		| CMD_A_VALID
+		| CMD_B_VALID
+		| (CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL <<
+		CMD_TRANS_SIZE_SHIFT)
+		| CMD_CE0;
+	if (!is_writing)
+		reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
+	else
+		reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
+	writel(reg_val, &info->reg->command);
+
+	/* Setup DMA engine */
+	reg_val = DMA_MST_CTRL_GO_ENABLE
+		| DMA_MST_CTRL_BURST_8WORDS
+		| DMA_MST_CTRL_EN_A_ENABLE
+		| DMA_MST_CTRL_EN_B_ENABLE;
+
+	if (!is_writing)
+		reg_val |= DMA_MST_CTRL_DIR_READ;
+	else
+		reg_val |= DMA_MST_CTRL_DIR_WRITE;
+
+	writel(reg_val, &info->reg->dma_mst_ctrl);
+
+	start_command(info->reg);
+
+	if (!nand_waitfor_cmd_completion(info->reg)) {
+		if (!is_writing)
+			printf("Read Page 0x%X timeout ", page);
+		else
+			printf("Write Page 0x%X timeout ", page);
+		if (with_ecc)
+			printf("with ECC");
+		else
+			printf("without ECC");
+		printf("\n");
+		return -EIO;
+	}
+
+	if (with_ecc && !is_writing) {
+		memcpy(chip->oob_poi, tag_ptr,
+			config->skipped_spare_bytes);
+		memcpy(chip->oob_poi + free->offset,
+			tag_ptr + config->skipped_spare_bytes,
+			config->tag_bytes);
+		reg_val = (u32) check_ecc_error(info->reg, (u8 *) buf,
+			1 << chip->page_shift,
+			(u8 *) (tag_ptr + config->skipped_spare_bytes),
+			config->tag_bytes);
+		if (reg_val & ECC_TAG_ERROR)
+			printf("Read Page 0x%X tag ECC error\n", page);
+		if (reg_val & ECC_DATA_ERROR)
+			printf("Read Page 0x%X data ECC error\n",
+				page);
+		if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR))
+			return -EIO;
+	}
+	return 0;
+}
+
+/**
+ * Hardware ecc based page read function
+ *
+ * @param mtd	mtd info structure
+ * @param chip	nand chip info structure
+ * @param buf	buffer to store read data
+ * @param page	page number to read
+ * @return	0 when successfully completed
+ *		-EIO when command timeout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd,
+	struct nand_chip *chip, uint8_t *buf, int page)
+{
+	return nand_rw_page(mtd, chip, buf, page, 1, 0);
+}
+
+/**
+ * Hardware ecc based page write function
+ *
+ * @param mtd	mtd info structure
+ * @param chip	nand chip info structure
+ * @param buf	data buffer
+ */
+static void nand_write_page_hwecc(struct mtd_info *mtd,
+	struct nand_chip *chip, const uint8_t *buf)
+{
+	int page;
+	struct nand_info *info;
+
+	info = (struct nand_info *) chip->priv;
+
+	page = (readl(&info->reg->addr_reg1) >> 16) |
+		(readl(&info->reg->addr_reg2) << 16);
+
+	nand_rw_page(mtd, chip, (uint8_t *) buf, page, 1, 1);
+}
+
+
+/**
+ * Read raw page data without ecc
+ *
+ * @param mtd	mtd info structure
+ * @param chip	nand chip info structure
+ * @param buf	buffer to store read data
+ * @param page	page number to read
+ * @return	0 when successfully completed
+ *		-EINVAL when chip->oob_poi is not double-word aligned
+ *		-EIO when command timeout
+ */
+static int nand_read_page_raw(struct mtd_info *mtd,
+	struct nand_chip *chip, uint8_t *buf, int page)
+{
+	return nand_rw_page(mtd, chip, buf, page, 0, 0);
+}
+
+/**
+ * Raw page write function
+ *
+ * @param mtd	mtd info structure
+ * @param chip	nand chip info structure
+ * @param buf	data buffer
+ */
+static void nand_write_page_raw(struct mtd_info *mtd,
+		struct nand_chip *chip,	const uint8_t *buf)
+{
+	int page;
+	struct nand_info *info;
+
+	info = (struct nand_info *) chip->priv;
+	page = (readl(&info->reg->addr_reg1) >> 16) |
+		(readl(&info->reg->addr_reg2) << 16);
+
+	nand_rw_page(mtd, chip, (uint8_t *) buf, page, 0, 1);
+}
+
+/**
+ * OOB data read/write function
+ *
+ * @param mtd		mtd info structure
+ * @param chip		nand chip info structure
+ * @param page		page number to read
+ * @param with_ecc	1 to enable ECC, 0 to disable ECC
+ * @param is_writing	0 for read, 1 for write
+ * @return	0 when successfully completed
+ *		-EINVAL when chip->oob_poi is not double-word aligned
+ *		-EIO when command timeout
+ */
+static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
+	int page, int with_ecc, int is_writing)
+{
+	u32 reg_val;
+	int tag_size;
+	struct nand_oobfree *free = chip->ecc.layout->oobfree;
+	struct nand_info *info;
+
+	if (((int) chip->oob_poi) & 0x03)
+		return -EINVAL;
+
+	info = (struct nand_info *) chip->priv;
+	stop_command(info->reg);
+
+	writel((u32) chip->oob_poi, &info->reg->tag_ptr);
+
+	set_bus_width_page_size(&info->config, &reg_val);
+
+	/* Set ECC selection */
+	tag_size = mtd->oobsize;
+	if (with_ecc)
+		reg_val |= CFG_ECC_EN_TAG_ENABLE;
+	else
+		reg_val |= (CFG_ECC_EN_TAG_DISABLE);
+
+	reg_val |= ((tag_size - 1) |
+		CFG_SKIP_SPARE_DISABLE |
+		CFG_HW_ECC_CORRECTION_DISABLE |
+		CFG_HW_ECC_DISABLE);
+	writel(reg_val, &info->reg->config);
+
+	if (!is_writing)
+		invalidate_dcache_range((unsigned long) chip->oob_poi,
+			((unsigned long) chip->oob_poi) + tag_size);
+	else
+		flush_dcache_range((unsigned long) chip->oob_poi,
+			((unsigned long) chip->oob_poi) + tag_size);
+
+	writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
+	if (is_writing && with_ecc)
+		tag_size -= info->config.tag_ecc_bytes;
+
+	writel(tag_size - 1, &info->reg->dma_cfg_b);
+
+	nand_clear_interrupt_status(info->reg);
+
+	reg_val = CMD_CLE | CMD_ALE
+		| CMD_SEC_CMD
+		| (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
+		| CMD_B_VALID
+		| CMD_CE0;
+	if (!is_writing)
+		reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
+	else
+		reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
+	writel(reg_val, &info->reg->command);
+
+	/* Setup DMA engine */
+	reg_val = DMA_MST_CTRL_GO_ENABLE
+		| DMA_MST_CTRL_BURST_8WORDS
+		| DMA_MST_CTRL_EN_B_ENABLE;
+	if (!is_writing)
+		reg_val |= DMA_MST_CTRL_DIR_READ;
+	else
+		reg_val |= DMA_MST_CTRL_DIR_WRITE;
+
+	writel(reg_val, &info->reg->dma_mst_ctrl);
+
+	start_command(info->reg);
+
+	if (!nand_waitfor_cmd_completion(info->reg)) {
+		if (!is_writing)
+			printf("Read OOB of Page 0x%X timeout\n", page);
+		else
+			printf("Write OOB of Page 0x%X timeout\n", page);
+		return -EIO;
+	}
+
+	if (with_ecc && !is_writing) {
+		reg_val = (u32) check_ecc_error(info->reg, 0, 0,
+			(u8 *) (chip->oob_poi + free->offset),
+			info->config.tag_bytes);
+		if (reg_val & ECC_TAG_ERROR)
+			printf("Read OOB of Page 0x%X tag ECC error\n", page);
+	}
+	return 0;
+}
+
+/**
+ * OOB data read function
+ *
+ * @param mtd		mtd info structure
+ * @param chip		nand chip info structure
+ * @param page		page number to read
+ * @param sndcmd	flag whether to issue read command or not
+ * @return	1 - issue read command next time
+ *		0 - not to issue
+ */
+static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+	int page, int sndcmd)
+{
+	if (sndcmd) {
+		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+		sndcmd = 0;
+	}
+	nand_rw_oob(mtd, chip, page, 0, 0);
+	return sndcmd;
+}
+
+/**
+ * OOB data write function
+ *
+ * @param mtd	mtd info structure
+ * @param chip	nand chip info structure
+ * @param page	page number to write
+ * @return	0 when successfully completed
+ *		-EINVAL when chip->oob_poi is not double-word aligned
+ *		-EIO when command timeout
+ */
+static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+	int page)
+{
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+
+	return nand_rw_oob(mtd, chip, page, 0, 1);
+}
+
+/**
+ * Set up NAND memory timings according to the provided parameters
+ *
+ * @param timing	Timing parameters
+ * @param reg		NAND controller register address
+ */
+static void setup_timing(unsigned timing[FDT_NAND_TIMING_COUNT],
+			 struct nand_ctlr *reg)
+{
+	u32 reg_val, clk_rate, clk_period, time_val;
+
+	clk_rate = (u32) clock_get_periph_rate(PERIPH_ID_NDFLASH,
+		CLOCK_ID_PERIPH) / 1000000;
+	clk_period = 1000 / clk_rate;
+	reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
+		TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK;
+	reg_val |= ((timing[FDT_NAND_TWB] / clk_period) <<
+		TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK;
+	time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period;
+	if (time_val > 2)
+		reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) &
+			TIMING_TCR_TAR_TRR_CNT_MASK;
+	reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) <<
+		TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK;
+	time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period;
+	if (time_val > 1)
+		reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) &
+			TIMING_TCS_CNT_MASK;
+	reg_val |= ((timing[FDT_NAND_TWH] / clk_period) <<
+		TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK;
+	reg_val |= ((timing[FDT_NAND_TWP] / clk_period) <<
+		TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK;
+	reg_val |= ((timing[FDT_NAND_TRH] / clk_period) <<
+		TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK;
+	reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
+		TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK;
+	writel(reg_val, &reg->timing);
+
+	reg_val = 0;
+	time_val = timing[FDT_NAND_TADL] / clk_period;
+	if (time_val > 2)
+		reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK;
+	writel(reg_val, &reg->timing2);
+}
+
+/**
+ * Decode NAND parameters from the device tree
+ *
+ * @param blob	Device tree blob
+ * @param node	Node containing "nand-flash" compatble node
+ * @return 0 if ok, -ve on error (FDT_ERR_...)
+ */
+int fdt_decode_nand(const void *blob, int node, struct fdt_nand *config)
+{
+	int err;
+
+	config->reg = (struct nand_ctlr *)fdtdec_get_addr(blob, node, "reg");
+	config->enabled = fdtdec_get_is_enabled(blob, node);
+	config->width = fdtdec_get_int(blob, node, "width", 8);
+	err = fdtdec_decode_gpio(blob, node, "wp-gpios", &config->wp_gpio);
+	if (err)
+		return err;
+	err = fdtdec_get_int_array(blob, node, "nvidia,timing",
+			config->timing, FDT_NAND_TIMING_COUNT);
+	if (err < 0)
+		return err;
+
+	/* Now look up the controller and decode that */
+	node = fdt_next_node(blob, node, NULL);
+	if (node < 0)
+		return node;
+
+	config->page_data_bytes = fdtdec_get_int(blob, node,
+						"page-data-bytes", -1);
+	config->tag_ecc_bytes = fdtdec_get_int(blob, node,
+						"tag-ecc-bytes", -1);
+	config->tag_bytes = fdtdec_get_int(blob, node, "tag-bytes", -1);
+	config->data_ecc_bytes = fdtdec_get_int(blob, node,
+						"data-ecc-bytes", -1);
+	config->skipped_spare_bytes = fdtdec_get_int(blob, node,
+						"skipped-spare-bytes", -1);
+	config->page_spare_bytes = fdtdec_get_int(blob, node,
+						"page-spare-bytes", -1);
+	if (config->page_data_bytes == -1 || config->tag_ecc_bytes == -1 ||
+		config->tag_bytes == -1 || config->data_ecc_bytes == -1 ||
+		config->skipped_spare_bytes == -1 ||
+		config->page_spare_bytes == -1)
+		return -FDT_ERR_NOTFOUND;
+
+	return 0;
+}
+
+/**
+ * Board-specific NAND initialization
+ *
+ * @param nand	nand chip info structure
+ * @return 0, after initialized, -1 on error
+ */
+int board_nand_init(struct nand_chip *nand)
+{
+	struct nand_info *info = &nand_ctrl;
+	struct fdt_nand *config = &info->config;
+	int node;
+
+	node = fdtdec_next_compatible(gd->fdt_blob, 0,
+				      COMPAT_NVIDIA_TEGRA20_NAND);
+	if (node < 0)
+		return -1;
+	if (fdt_decode_nand(gd->fdt_blob, node, config)) {
+		printf("Could not decode nand-flash in device tree\n");
+		return -1;
+	}
+	if (!config->enabled)
+		return -1;
+	info->reg = config->reg;
+	eccoob.eccbytes = config->data_ecc_bytes + config->tag_ecc_bytes;
+	eccoob.oobavail = config->tag_bytes;
+	eccoob.oobfree[0].offset = config->skipped_spare_bytes +
+				config->data_ecc_bytes;
+	eccoob.oobfree[0].length = config->tag_bytes;
+
+	nand->ecc.mode = NAND_ECC_HW;
+	nand->ecc.layout = &eccoob;
+	nand->ecc.size = config->page_data_bytes;
+	nand->ecc.bytes = config->page_spare_bytes;
+
+	nand->options = LP_OPTIONS;
+	nand->cmdfunc = nand_command;
+	nand->read_byte = read_byte;
+	nand->read_buf = read_buf;
+	nand->write_buf = write_buf;
+	nand->ecc.read_page = nand_read_page_hwecc;
+	nand->ecc.write_page = nand_write_page_hwecc;
+	nand->ecc.read_page_raw = nand_read_page_raw;
+	nand->ecc.write_page_raw = nand_write_page_raw;
+	nand->ecc.read_oob = nand_read_oob;
+	nand->ecc.write_oob = nand_write_oob;
+	nand->cmd_ctrl = nand_hwcontrol;
+	nand->dev_ready  = nand_dev_ready;
+	nand->priv = &nand_ctrl;
+
+	/* Adjust controller clock rate */
+	clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, 52000000);
+
+	/* Adjust timing for NAND device */
+	setup_timing(config->timing, info->reg);
+
+	funcmux_select(PERIPH_ID_NDFLASH, FUNCMUX_DEFAULT);
+	fdtdec_setup_gpio(&config->wp_gpio);
+	gpio_direction_output(config->wp_gpio.gpio, 1);
+
+	return 0;
+}
diff --git a/drivers/mtd/nand/tegra2_nand.h b/drivers/mtd/nand/tegra2_nand.h
new file mode 100644
index 0000000..58c8a95
--- /dev/null
+++ b/drivers/mtd/nand/tegra2_nand.h
@@ -0,0 +1,303 @@
+/*
+ * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+
+/* Oh dear I suspect no one will like these Bit values? */
+enum {
+	Bit0 = 1 << 0,
+	Bit1 = 1 << 1,
+	Bit2 = 1 << 2,
+	Bit3 = 1 << 3,
+	Bit4 = 1 << 4,
+	Bit5 = 1 << 5,
+	Bit6 = 1 << 6,
+	Bit7 = 1 << 7,
+	Bit8 = 1 << 8,
+	Bit9 = 1 << 9,
+	Bit10 = 1 << 10,
+	Bit11 = 1 << 11,
+	Bit12 = 1 << 12,
+	Bit13 = 1 << 13,
+	Bit14 = 1 << 14,
+	Bit15 = 1 << 15,
+	Bit16 = 1 << 16,
+	Bit17 = 1 << 17,
+	Bit18 = 1 << 18,
+	Bit19 = 1 << 19,
+	Bit20 = 1 << 20,
+	Bit21 = 1 << 21,
+	Bit22 = 1 << 22,
+	Bit23 = 1 << 23,
+	Bit24 = 1 << 24,
+	Bit25 = 1 << 25,
+	Bit26 = 1 << 26,
+	Bit27 = 1 << 27,
+	Bit28 = 1 << 28,
+	Bit29 = 1 << 29,
+	Bit30 = 1 << 30,
+	Bit31 = 1 << 31
+};
+
+/* register offset */
+#define COMMAND_0		0x00
+#define CMD_GO		Bit31
+#define CMD_CLE		Bit30
+#define CMD_ALE		Bit29
+#define CMD_PIO		Bit28
+#define CMD_TX		Bit27
+#define CMD_RX		Bit26
+#define CMD_SEC_CMD	Bit25
+#define CMD_AFT_DAT_MASK	Bit24
+#define CMD_AFT_DAT_DISABLE	0
+#define CMD_AFT_DAT_ENABLE	Bit24
+#define CMD_TRANS_SIZE_SHIFT	20
+enum {
+	CMD_TRANS_SIZE_BYTES1 = 0,
+	CMD_TRANS_SIZE_BYTES2,
+	CMD_TRANS_SIZE_BYTES3,
+	CMD_TRANS_SIZE_BYTES4,
+	CMD_TRANS_SIZE_BYTES5,
+	CMD_TRANS_SIZE_BYTES6,
+	CMD_TRANS_SIZE_BYTES7,
+	CMD_TRANS_SIZE_BYTES8,
+	CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL
+};
+
+#define CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL	8
+#define CMD_A_VALID	Bit19
+#define CMD_B_VALID	Bit18
+#define CMD_RD_STATUS_CHK	Bit17
+#define CMD_R_BSY_CHK	Bit16
+#define CMD_CE7		Bit15
+#define CMD_CE6		Bit14
+#define CMD_CE5		Bit13
+#define CMD_CE4		Bit12
+#define CMD_CE3		Bit11
+#define CMD_CE2		Bit10
+#define CMD_CE1		Bit9
+#define CMD_CE0		Bit8
+#define CMD_CLE_BYTE_SIZE_SHIFT	4
+enum {
+	CMD_CLE_BYTES1 = 0,
+	CMD_CLE_BYTES2,
+	CMD_CLE_BYTES3,
+	CMD_CLE_BYTES4,
+};
+#define CMD_ALE_BYTE_SIZE_SHIFT	0
+enum {
+	CMD_ALE_BYTES1 = 0,
+	CMD_ALE_BYTES2,
+	CMD_ALE_BYTES3,
+	CMD_ALE_BYTES4,
+	CMD_ALE_BYTES5,
+	CMD_ALE_BYTES6,
+	CMD_ALE_BYTES7,
+	CMD_ALE_BYTES8
+};
+
+#define STATUS_0		0x04
+#define STATUS_RBSY0	Bit8
+
+#define ISR_0			0x08
+#define ISR_IS_CMD_DONE	Bit5
+#define ISR_IS_ECC_ERR	Bit4
+
+#define IER_0			0x0C
+
+#define CFG_0		0x10
+#define CFG_HW_ECC_MASK		Bit31
+#define CFG_HW_ECC_DISABLE		0
+#define CFG_HW_ECC_ENABLE		Bit31
+#define CFG_HW_ECC_SEL_MASK		Bit30
+#define CFG_HW_ECC_SEL_HAMMING	0
+#define CFG_HW_ECC_SEL_RS		Bit30
+#define CFG_HW_ECC_CORRECTION_MASK	Bit29
+#define CFG_HW_ECC_CORRECTION_DISABLE	0
+#define CFG_HW_ECC_CORRECTION_ENABLE	Bit29
+#define CFG_PIPELINE_EN_MASK		Bit28
+#define CFG_PIPELINE_EN_DISABLE	0
+#define CFG_PIPELINE_EN_ENABLE	Bit28
+#define CFG_ECC_EN_TAG_MASK		Bit27
+#define CFG_ECC_EN_TAG_DISABLE	0
+#define CFG_ECC_EN_TAG_ENABLE	Bit27
+#define CFG_TVALUE_MASK		(Bit25 | Bit24)
+enum {
+	CFG_TVAL4 = 0 << 24,
+	CFG_TVAL6 = 1 << 24,
+	CFG_TVAL8 = 2 << 24
+};
+#define CFG_SKIP_SPARE_MASK		Bit23
+#define CFG_SKIP_SPARE_DISABLE	0
+#define CFG_SKIP_SPARE_ENABLE	Bit23
+#define CFG_COM_BSY_MASK		Bit22
+#define CFG_COM_BSY_DISABLE		0
+#define CFG_COM_BSY_ENABLE		Bit22
+#define CFG_BUS_WIDTH_MASK		Bit21
+#define CFG_BUS_WIDTH_8BIT		0
+#define CFG_BUS_WIDTH_16BIT		Bit21
+#define CFG_LPDDR1_MODE_MASK		Bit20
+#define CFG_LPDDR1_MODE_DISABLE	0
+#define CFG_LPDDR1_MODE_ENABLE	Bit20
+#define CFG_EDO_MODE_MASK		Bit19
+#define CFG_EDO_MODE_DISABLE		0
+#define CFG_EDO_MODE_ENABLE		Bit19
+#define CFG_PAGE_SIZE_SEL_MASK	(Bit18 | Bit17 | Bit16)
+enum {
+	CFG_PAGE_SIZE_256	= 0 << 16,
+	CFG_PAGE_SIZE_512	= 1 << 16,
+	CFG_PAGE_SIZE_1024	= 2 << 16,
+	CFG_PAGE_SIZE_2048	= 3 << 16,
+	CFG_PAGE_SIZE_4096	= 4 << 16
+};
+#define CFG_SKIP_SPARE_SEL_MASK		(Bit15 | Bit14)
+enum {
+	CFG_SKIP_SPARE_SEL_4		= 0 << 14,
+	CFG_SKIP_SPARE_SEL_8		= 1 << 14,
+	CFG_SKIP_SPARE_SEL_12	= 2 << 14,
+	CFG_SKIP_SPARE_SEL_16	= 3 << 14
+};
+#define CFG_TAG_BYTE_SIZE_MASK	0x1FF
+
+#define TIMING_0		0x14
+#define TIMING_TRP_RESP_CNT_SHIFT	28
+#define TIMING_TRP_RESP_CNT_MASK	(Bit31 | Bit30 | Bit29 | Bit28)
+#define TIMING_TWB_CNT_SHIFT		24
+#define TIMING_TWB_CNT_MASK		(Bit27 | Bit26 | Bit25 | Bit24)
+#define TIMING_TCR_TAR_TRR_CNT_SHIFT	20
+#define TIMING_TCR_TAR_TRR_CNT_MASK	(Bit23 | Bit22 | Bit21 | Bit20)
+#define TIMING_TWHR_CNT_SHIFT		16
+#define TIMING_TWHR_CNT_MASK		(Bit19 | Bit18 | Bit17 | Bit16)
+#define TIMING_TCS_CNT_SHIFT		14
+#define TIMING_TCS_CNT_MASK		(Bit15 | Bit14)
+#define TIMING_TWH_CNT_SHIFT		12
+#define TIMING_TWH_CNT_MASK		(Bit13 | Bit12)
+#define TIMING_TWP_CNT_SHIFT		8
+#define TIMING_TWP_CNT_MASK		(Bit11 | Bit10 | Bit9 | Bit8)
+#define TIMING_TRH_CNT_SHIFT		4
+#define TIMING_TRH_CNT_MASK		(Bit5 | Bit4)
+#define TIMING_TRP_CNT_SHIFT		0
+#define TIMING_TRP_CNT_MASK		(Bit3 | Bit2 | Bit1 | Bit0)
+
+#define RESP_0			0x18
+
+#define TIMING2_0		0x1C
+#define TIMING2_TADL_CNT_SHIFT		0
+#define TIMING2_TADL_CNT_MASK		(Bit3 | Bit2 | Bit1 | Bit0)
+
+#define CMD_REG1_0		0x20
+#define CMD_REG2_0		0x24
+#define ADDR_REG1_0		0x28
+#define ADDR_REG2_0		0x2C
+
+#define DMA_MST_CTRL_0		0x30
+#define DMA_MST_CTRL_GO_MASK		Bit31
+#define DMA_MST_CTRL_GO_DISABLE		0
+#define DMA_MST_CTRL_GO_ENABLE		Bit31
+#define DMA_MST_CTRL_DIR_MASK		Bit30
+#define DMA_MST_CTRL_DIR_READ		0
+#define DMA_MST_CTRL_DIR_WRITE		Bit30
+#define DMA_MST_CTRL_PERF_EN_MASK	Bit29
+#define DMA_MST_CTRL_PERF_EN_DISABLE	0
+#define DMA_MST_CTRL_PERF_EN_ENABLE	Bit29
+#define DMA_MST_CTRL_REUSE_BUFFER_MASK	Bit27
+#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE	0
+#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE	Bit27
+#define DMA_MST_CTRL_BURST_SIZE_MASK	(Bit26 | Bit25 | Bit24)
+enum {
+	DMA_MST_CTRL_BURST_1WORDS	= 2 << 24,
+	DMA_MST_CTRL_BURST_4WORDS	= 3 << 24,
+	DMA_MST_CTRL_BURST_8WORDS	= 4 << 24,
+	DMA_MST_CTRL_BURST_16WORDS	= 5 << 24
+};
+#define DMA_MST_CTRL_IS_DMA_DONE	Bit20
+#define DMA_MST_CTRL_EN_A_MASK		Bit2
+#define DMA_MST_CTRL_EN_A_DISABLE	0
+#define DMA_MST_CTRL_EN_A_ENABLE	Bit2
+#define DMA_MST_CTRL_EN_B_MASK		Bit1
+#define DMA_MST_CTRL_EN_B_DISABLE	0
+#define DMA_MST_CTRL_EN_B_ENABLE	Bit1
+
+#define DMA_CFG_A_0		0x34
+#define DMA_CFG_B_0		0x38
+#define FIFO_CTRL_0		0x3C
+#define DATA_BLOCK_PTR_0	0x40
+#define TAG_PTR_0		0x44
+#define ECC_PTR_0		0x48
+
+#define DEC_STATUS_0		0x4C
+#define DEC_STATUS_A_ECC_FAIL		Bit1
+#define DEC_STATUS_B_ECC_FAIL		Bit0
+
+#define BCH_CONFIG_0		0xCC
+#define BCH_CONFIG_BCH_TVALUE_MASK	(Bit5 | Bit4)
+enum {
+	BCH_CONFIG_BCH_TVAL4	= 0 << 4,
+	BCH_CONFIG_BCH_TVAL8	= 1 << 4,
+	BCH_CONFIG_BCH_TVAL14	= 2 << 4,
+	BCH_CONFIG_BCH_TVAL16	= 3 << 4
+};
+#define BCH_CONFIG_BCH_ECC_MASK		Bit0
+#define BCH_CONFIG_BCH_ECC_DISABLE	0
+#define BCH_CONFIG_BCH_ECC_ENABLE	Bit0
+
+#define BCH_DEC_RESULT_0	0xD0
+#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK	Bit8
+#define BCH_DEC_RESULT_PAGE_COUNT_MASK		0xFF
+
+#define BCH_DEC_STATUS_BUF_0	0xD4
+#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK	0xFF000000
+#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK	0x00FF0000
+#define BCH_DEC_STATUS_FAIL_TAG_MASK		Bit14
+#define BCH_DEC_STATUS_CORR_TAG_MASK		Bit13
+#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (Bit12 | Bit11 | Bit10 | Bit9 | Bit8)
+#define BCH_DEC_STATUS_PAGE_NUMBER_MASK		0xFF
+
+#define LP_OPTIONS (NAND_NO_READRDY | NAND_NO_AUTOINCR)
+
+struct nand_ctlr {
+	u32	command;	/* offset 00h */
+	u32	status;		/* offset 04h */
+	u32	isr;		/* offset 08h */
+	u32	ier;		/* offset 0Ch */
+	u32	config;		/* offset 10h */
+	u32	timing;		/* offset 14h */
+	u32	resp;		/* offset 18h */
+	u32	timing2;	/* offset 1Ch */
+	u32	cmd_reg1;	/* offset 20h */
+	u32	cmd_reg2;	/* offset 24h */
+	u32	addr_reg1;	/* offset 28h */
+	u32	addr_reg2;	/* offset 2Ch */
+	u32	dma_mst_ctrl;	/* offset 30h */
+	u32	dma_cfg_a;	/* offset 34h */
+	u32	dma_cfg_b;	/* offset 38h */
+	u32	fifo_ctrl;	/* offset 3Ch */
+	u32	data_block_ptr;	/* offset 40h */
+	u32	tag_ptr;	/* offset 44h */
+	u32	resv1;		/* offset 48h */
+	u32	dec_status;	/* offset 4Ch */
+	u32	hwstatus_cmd;	/* offset 50h */
+	u32	hwstatus_mask;	/* offset 54h */
+	u32	resv2[29];
+	u32	bch_config;	/* offset CCh */
+	u32	bch_dec_result;	/* offset D0h */
+	u32	bch_dec_status_buf;
+				/* offset D4h */
+};
diff --git a/include/fdtdec.h b/include/fdtdec.h
index a14f2d7..64e2f88 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -62,6 +62,7 @@ enum fdt_compat_id {
 	COMPAT_NVIDIA_TEGRA20_DVC,	/* Tegra2 dvc (really just i2c) */
 	COMPAT_NVIDIA_TEGRA20_EMC,	/* Tegra2 memory controller */
 	COMPAT_NVIDIA_TEGRA20_EMC_TABLE, /* Tegra2 memory timing table */
+	COMPAT_NVIDIA_TEGRA20_NAND,	/* Tegra2 NAND controller */
 
 	COMPAT_COUNT,
 };
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 5d97e2a..db377c7 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -42,6 +42,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
 	COMPAT(NVIDIA_TEGRA20_DVC, "nvidia,tegra20-i2c-dvc"),
 	COMPAT(NVIDIA_TEGRA20_EMC, "nvidia,tegra20-emc"),
 	COMPAT(NVIDIA_TEGRA20_EMC_TABLE, "nvidia,tegra20-emc-table"),
+	COMPAT(NVIDIA_TEGRA20_NAND, "nvidia,tegra20-nand"),
 };
 
 const char *fdtdec_get_compatible(enum fdt_compat_id id)
-- 
1.7.7.3



More information about the U-Boot mailing list