All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] mtd: rawnand: cortina_nand: Add Cortina CAxxxx SoC support
@ 2020-12-08 19:37 Alex Nemirovsky
  2020-12-08 19:37 ` [PATCH 2/2] board: presidio: Add Parallel NAND support Alex Nemirovsky
  2020-12-11 20:54 ` [PATCH 1/2] mtd: rawnand: cortina_nand: Add Cortina CAxxxx SoC support Tom Rini
  0 siblings, 2 replies; 5+ messages in thread
From: Alex Nemirovsky @ 2020-12-08 19:37 UTC (permalink / raw)
  To: u-boot

From: Kate Liu <kate.liu@cortina-access.com>

Add Cortina Access parallel Nand support for CAxxxx SOCs

Signed-off-by: Kate Liu <kate.liu@cortina-access.com>
Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
CC: Tom Rini <trini@konsulko.com>
CC: Scott Wood <oss@buserror.net>
---

 MAINTAINERS                         |    2 +
 drivers/mtd/nand/raw/Kconfig        |   12 +
 drivers/mtd/nand/raw/Makefile       |    1 +
 drivers/mtd/nand/raw/cortina_nand.c | 1390 +++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/raw/cortina_nand.h |  293 ++++++++
 5 files changed, 1698 insertions(+)
 create mode 100644 drivers/mtd/nand/raw/cortina_nand.c
 create mode 100644 drivers/mtd/nand/raw/cortina_nand.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 2625fc6..a002263 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -197,6 +197,8 @@ F:	drivers/led/led_cortina.c
 F:	drivers/mmc/ca_dw_mmc.c
 F:	drivers/i2c/i2c-cortina.c
 F:	drivers/i2c/i2c-cortina.h
+F:	drivers/mtd/nand/raw/cortina_nand.c
+F:	drivers/mtd/nand/raw/cortina_nand.h
 
 ARM/CZ.NIC TURRIS MOX SUPPORT
 M:	Marek Behun <marek.behun@nic.cz>
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 3cf3b14..ed151ee 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -321,6 +321,18 @@ config NAND_STM32_FMC2
 	  The controller supports a maximum 8k page size and supports
 	  a maximum 8-bit correction error per sector of 512 bytes.
 
+config CORTINA_NAND
+	bool "Support for NAND controller on Cortina-Access SoCs"
+	depends on CORTINA_PLATFORM
+	select SYS_NAND_SELF_INIT
+	select DM_MTD
+	imply CMD_NAND
+	help
+	  Enables support for NAND Flash chips on Coartina-Access SoCs platform
+	  This controller is found on Presidio/Venus SoCs.
+	  The controller supports a maximum 8k page size and supports
+	  a maximum 40-bit error correction per sector of 1024 bytes.
+
 comment "Generic NAND options"
 
 config SYS_NAND_BLOCK_SIZE
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 24c51b6..f3f0e15 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_NAND_PLAT) += nand_plat.o
 obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
 obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
 obj-$(CONFIG_NAND_STM32_FMC2) += stm32_fmc2_nand.o
+obj-$(CONFIG_CORTINA_NAND) += cortina_nand.o
 
 else  # minimal SPL drivers
 
diff --git a/drivers/mtd/nand/raw/cortina_nand.c b/drivers/mtd/nand/raw/cortina_nand.c
new file mode 100644
index 0000000..480ef63
--- /dev/null
+++ b/drivers/mtd/nand/raw/cortina_nand.c
@@ -0,0 +1,1390 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Cortina Access Inc..
+ */
+
+#include <common.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/sizes.h>
+#include <log.h>
+#include <asm/io.h>
+#include <memalign.h>
+#include <nand.h>
+#include <dm/device_compat.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/errno.h>
+#include <asm/gpio.h>
+#include <fdtdec.h>
+#include <bouncebuf.h>
+#include <dm.h>
+#include "cortina_nand.h"
+
+static unsigned int *pread, *pwrite;
+
+static const struct udevice_id cortina_nand_dt_ids[] = {
+	{
+	 .compatible = "cortina,ca-nand",
+	 },
+	{ /* sentinel */ }
+};
+
+static struct nand_ecclayout eccoob;
+
+/* Information about an attached NAND chip */
+struct fdt_nand {
+	int enabled;		/* 1 to enable, 0 to disable */
+	s32 width;		/* bit width, must be 8 */
+	u32 nand_ecc_strength;
+};
+
+struct nand_drv {
+	u32 fifo_index;
+	struct nand_ctlr *reg;
+	struct dma_global *dma_glb;
+	struct dma_ssp *dma_nand;
+	struct tx_descriptor_t *tx_desc;
+	struct rx_descriptor_t *rx_desc;
+	struct fdt_nand config;
+	unsigned int flash_base;
+};
+
+struct ca_nand_info {
+	struct udevice *dev;
+	struct nand_drv nand_ctrl;
+	struct nand_chip nand_chip;
+};
+
+/**
+ * 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, unsigned int mask)
+{
+	unsigned int reg_v = 0;
+
+	if (readl_poll_timeout(&reg->flash_flash_access_start, reg_v,
+			       !(reg_v & mask), (FLASH_LONG_DELAY << 2))) {
+		pr_err("Nand CMD timeout!\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * Read one byte from the chip
+ *
+ * @param mtd	MTD device structure
+ * @return	data byte
+ *
+ * Read function for 8bit bus-width
+ */
+static uint8_t read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_drv *info;
+	u8 ret_v;
+
+	info = (struct nand_drv *)nand_get_controller_data(chip);
+
+	clrsetbits_le32(&info->reg->flash_flash_access_start, GENMASK(31, 0),
+			NFLASH_GO | NFLASH_RD);
+
+	if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+		printf("%s: Command timeout\n", __func__);
+
+	ret_v = readl(&info->reg->flash_nf_data) >> (8 * info->fifo_index++);
+	info->fifo_index %= 4;
+
+	return (uint8_t)ret_v;
+}
+
+/**
+ * Read len bytes from the chip into a buffer
+ *
+ * @param mtd	MTD device structure
+ * @param buf	buffer to store data to
+ * @param len	number of bytes to read
+ *
+ * Read function for 8bit bus-width
+ */
+static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	int i;
+	unsigned int reg;
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+
+	for (i = 0; i < len; i++) {
+		clrsetbits_le32(&info->reg->flash_flash_access_start,
+				GENMASK(31, 0), NFLASH_GO | NFLASH_RD);
+
+		if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+			printf("%s: Command timeout\n", __func__);
+
+		reg = readl(&info->reg->flash_nf_data) >>
+		    (8 * info->fifo_index++);
+		memcpy(buf + i, &reg, 1);
+		info->fifo_index %= 4;
+	}
+}
+
+/**
+ * Check READY pin 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)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int reg_val;
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+
+	reg_val = readl(&info->reg->flash_status);
+	if (reg_val & NFLASH_READY)
+		return 1;
+	else
+		return 0;
+}
+
+/* Dummy implementation: we don't support multiple chips */
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+	switch (chipnr) {
+	case -1:
+	case 0:
+		break;
+
+	default:
+		WARN_ON(chipnr);
+	}
+}
+
+int init_nand_dma(struct nand_chip *nand)
+{
+	int i;
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(nand);
+
+	setbits_le32(&info->dma_glb->dma_glb_dma_lso_ctrl, TX_DMA_ENABLE);
+	setbits_le32(&info->dma_glb->dma_glb_dma_ssp_rx_ctrl,
+		     TX_DMA_ENABLE | DMA_CHECK_OWNER);
+	setbits_le32(&info->dma_glb->dma_glb_dma_ssp_tx_ctrl,
+		     RX_DMA_ENABLE | DMA_CHECK_OWNER);
+
+	info->tx_desc = malloc_cache_aligned((sizeof(struct tx_descriptor_t) *
+					      CA_DMA_DESC_NUM));
+	info->rx_desc = malloc_cache_aligned((sizeof(struct rx_descriptor_t) *
+					      CA_DMA_DESC_NUM));
+
+	if (!info->rx_desc && info->tx_desc) {
+		printf("Fail to alloc DMA descript!\n");
+		kfree(info->tx_desc);
+		return -ENOMEM;
+	} else if (info->rx_desc && !info->tx_desc) {
+		printf("Fail to alloc DMA descript!\n");
+		kfree(info->tx_desc);
+		return -ENOMEM;
+	}
+
+	/* set RX DMA base address and depth */
+	clrsetbits_le32(&info->dma_nand->dma_q_rxq_base_depth,
+			GENMASK(31, 4), (uintptr_t)info->rx_desc);
+	clrsetbits_le32(&info->dma_nand->dma_q_rxq_base_depth,
+			GENMASK(3, 0), CA_DMA_DEPTH);
+
+	/* set TX DMA base address and depth */
+	clrsetbits_le32(&info->dma_nand->dma_q_txq_base_depth,
+			GENMASK(31, 4), (uintptr_t)info->tx_desc);
+	clrsetbits_le32(&info->dma_nand->dma_q_txq_base_depth,
+			GENMASK(3, 0), CA_DMA_DEPTH);
+
+	memset((unsigned char *)info->tx_desc, 0,
+	       (sizeof(struct tx_descriptor_t) * CA_DMA_DESC_NUM));
+	memset((unsigned char *)info->rx_desc, 0,
+	       (sizeof(struct rx_descriptor_t) * CA_DMA_DESC_NUM));
+
+	for (i = 0; i < CA_DMA_DESC_NUM; i++) {
+		/* set owner bit as SW */
+		info->tx_desc[i].own = 1;
+		/* enable Scatter-Gather memory copy */
+		info->tx_desc[i].sgm = 0x1;
+	}
+
+	return 0;
+}
+
+/**
+ * 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 ca_nand_command(struct mtd_info *mtd, unsigned int command,
+			    int column, int page_addr)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_drv *info;
+	unsigned int reg_v = 0;
+	u32 cmd = 0, cnt = 0, addr1 = 0, addr2 = 0;
+	int ret;
+
+	info = (struct nand_drv *)nand_get_controller_data(chip);
+	/*
+	 * Write out the command to the device.
+	 *
+	 * Only command NAND_CMD_RESET or NAND_CMD_READID will come
+	 * here before mtd->writesize is initialized.
+	 */
+
+	/* Emulate NAND_CMD_READOOB */
+	if (command == NAND_CMD_READOOB) {
+		assert(mtd->writesize != 0);
+		column += mtd->writesize;
+		command = NAND_CMD_READ0;
+	}
+
+	/* Reset FIFO before issue new command */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+			ECC_RESET_ALL);
+	ret =
+	    readl_poll_timeout(&info->reg->flash_nf_ecc_reset, reg_v,
+			       !(reg_v & RESET_NFLASH_FIFO), FLASH_SHORT_DELAY);
+	if (ret) {
+		printf("FIFO reset timeout\n");
+		clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+				ECC_RESET_ALL);
+		udelay(10);
+	}
+
+	/* Reset FIFO index
+	 * Next read start from flash_nf_data[0]
+	 */
+	info->fifo_index = 0;
+
+	clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(11, 10),
+			NFLASH_REG_WIDTH_8);
+
+	/*
+	 * Program and erase have their own busy handlers
+	 * status and sequential in needs no delay
+	 */
+	switch (command) {
+	case NAND_CMD_READID:
+		/* Command */
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+				NAND_CMD_READID);
+		/* 1 byte CMD cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+				REG_CMD_COUNT_1TOGO);
+		/* 1 byte CMD cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+				REG_ADDR_COUNT_1);
+		/* Data cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+				REG_DATA_COUNT_DATA_4);
+		/* 0 OOB cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+				REG_OOB_COUNT_EMPTY);
+
+		/* addresses */
+		clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+				column & ADDR1_MASK2);
+		clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+				0);
+
+		/* clear FLASH_NF_ACCESS */
+		clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+				DISABLE_AUTO_RESET);
+
+		break;
+	case NAND_CMD_PARAM:
+		/* Command */
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+				NAND_CMD_PARAM);
+		/* 1 byte CMD cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+				REG_CMD_COUNT_1TOGO);
+		/* 1 byte ADDR cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+				REG_ADDR_COUNT_1);
+		/* Data cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+				(SZ_4K - 1) << 8);
+		/* 0 OOB cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+				REG_OOB_COUNT_EMPTY);
+
+		/* addresses */
+		clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+				column & ADDR1_MASK2);
+		clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+				0);
+
+		break;
+	case NAND_CMD_READ0:
+		if (chip->chipsize < SZ_32M) {
+			cmd = NAND_CMD_READ0;
+			cnt = REG_CMD_COUNT_1TOGO | REG_ADDR_COUNT_3;
+			addr1 = (((page_addr & ADDR1_MASK0) << 8));
+			addr2 = ((page_addr & ADDR2_MASK0) >> 24);
+		} else if (chip->chipsize >= SZ_32M &&
+			   (chip->chipsize <= SZ_128M)) {
+			cmd = NAND_CMD_READ0;
+			cnt = REG_ADDR_COUNT_4;
+			if (mtd->writesize > (REG_DATA_COUNT_512_DATA >> 8)) {
+				cmd |= (NAND_CMD_READSTART << 8);
+				cnt |= REG_CMD_COUNT_2TOGO;
+			} else {
+				cnt |= REG_CMD_COUNT_1TOGO;
+			}
+			addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+			addr2 = (page_addr >> 16);
+		} else {
+			cmd = NAND_CMD_READ0 | (NAND_CMD_READSTART << 8);
+			cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_5;
+			addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+			addr2 = (page_addr >> 16);
+		}
+
+		/* Command */
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+				cmd);
+		/* CMD & ADDR cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(7, 0), cnt);
+		/* Data cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+				(mtd->writesize - 1) << 8);
+		/* OOB cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+				(mtd->oobsize - 1) << 22);
+
+		/* addresses */
+		clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+				addr1);
+		clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+				addr2);
+
+		return;
+	case NAND_CMD_SEQIN:
+		if (chip->chipsize < SZ_32M) {
+			cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_3;
+			addr1 = (((page_addr & ADDR1_MASK0) << 8));
+			addr2 = ((page_addr & ADDR2_MASK0) >> 24);
+		} else if (chip->chipsize >= SZ_32M &&
+			   (chip->chipsize <= SZ_128M)) {
+			cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_4;
+			addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+			addr2 = (page_addr >> 16);
+		} else {
+			cnt = REG_CMD_COUNT_2TOGO | REG_ADDR_COUNT_5;
+			addr1 = ((page_addr << 16) | (column & ADDR1_MASK1));
+			addr2 = (page_addr >> 16);
+		}
+
+		/* Command */
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+				NAND_CMD_SEQIN | (NAND_CMD_PAGEPROG << 8));
+		/* CMD cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(7, 0), cnt);
+		/* Data cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+				(mtd->writesize - 1) << 8);
+		/* OOB cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+				(mtd->oobsize - 1) << 22);
+
+		/* addresses */
+		clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+				addr1);
+		clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+				addr2);
+
+		return;
+	case NAND_CMD_PAGEPROG:
+		return;
+	case NAND_CMD_ERASE1:
+		/* Command */
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+				NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8));
+		/* 2 byte CMD cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+				REG_CMD_COUNT_2TOGO);
+		/* 3 byte ADDR cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+				REG_ADDR_COUNT_3);
+		/* 0 Data cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+				REG_DATA_COUNT_EMPTY);
+		/* 0 OOB cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+				REG_OOB_COUNT_EMPTY);
+
+		/* addresses */
+		clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+				page_addr);
+		clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+				0);
+
+		/* Issue command */
+		clrsetbits_le32(&info->reg->flash_flash_access_start,
+				GENMASK(31, 0), NFLASH_GO | NFLASH_RD);
+		break;
+	case NAND_CMD_ERASE2:
+		return;
+	case NAND_CMD_STATUS:
+		/* Command */
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+				NAND_CMD_STATUS);
+		/* 1 byte CMD cycle */
+		clrbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0));
+		/* 0 byte Addr cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+				REG_ADDR_COUNT_EMPTY);
+		/* 1 Data cycle */
+		clrbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8));
+		/* 0 OOB cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+				REG_OOB_COUNT_EMPTY);
+
+		break;
+	case NAND_CMD_RESET:
+		/* Command */
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+				NAND_CMD_RESET);
+		/* 1 byte CMD cycle */
+		clrbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0));
+		/* 0 byte Addr cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(6, 4),
+				REG_ADDR_COUNT_EMPTY);
+		/* 0 Data cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(21, 8),
+				REG_DATA_COUNT_EMPTY);
+		/* 0 OOB cycle */
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(31, 22),
+				REG_OOB_COUNT_EMPTY);
+
+		/* addresses */
+		clrsetbits_le32(&info->reg->flash_nf_address_1, GENMASK(31, 0),
+				column & ADDR1_MASK2);
+		clrsetbits_le32(&info->reg->flash_nf_address_2, GENMASK(31, 0),
+				0);
+
+		/* Issue command */
+		clrsetbits_le32(&info->reg->flash_flash_access_start,
+				GENMASK(31, 0), NFLASH_GO | NFLASH_WT);
+
+		break;
+	case NAND_CMD_RNDOUT:
+	default:
+		printf("%s: Unsupported command %d\n", __func__, command);
+		return;
+	}
+
+	if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+		printf("Command 0x%02X timeout\n", command);
+}
+
+/**
+ * Set up NAND bus width and page size
+ *
+ * @param info		nand_info structure
+ * @return 0 if ok, -1 on error
+ */
+static int set_bus_width_page_size(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+
+	if (info->config.width == SZ_8) {
+		clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+				NFLASH_REG_WIDTH_8);
+	} else if (info->config.width == SZ_16) {
+		clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+				NFLASH_REG_WIDTH_16);
+	} else {
+		debug("%s: Unsupported bus width %d\n", __func__,
+		      info->config.width);
+		return -1;
+	}
+
+	if (mtd->writesize == SZ_512) {
+		setbits_le32(&info->reg->flash_type, FLASH_TYPE_512);
+	} else if (mtd->writesize == SZ_2K) {
+		setbits_le32(&info->reg->flash_type, FLASH_TYPE_2K);
+	} else if (mtd->writesize == SZ_4K) {
+		setbits_le32(&info->reg->flash_type, FLASH_TYPE_4K);
+	} else if (mtd->writesize == SZ_8K) {
+		setbits_le32(&info->reg->flash_type, FLASH_TYPE_8K);
+	} else {
+		debug("%s: Unsupported page size %d\n", __func__,
+		      mtd->writesize);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int ca_do_bch_correction(struct nand_chip *chip,
+				unsigned int err_num, u8 *buff_ptr, int i)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	unsigned int reg_v, err_loc0, err_loc1;
+	int k, max_bitflips;
+
+	for (k = 0; k < (err_num + 1) / 2; k++) {
+		reg_v = readl(&info->reg->flash_nf_bch_error_loc01 + k);
+		err_loc0 = reg_v & BCH_ERR_LOC_MASK;
+		err_loc1 = (reg_v >> 16) & BCH_ERR_LOC_MASK;
+
+		if (err_loc0 / 8 < BCH_DATA_UNIT) {
+			printf("pdata[%x]:%x =>", ((i / chip->ecc.bytes) *
+				chip->ecc.size + ((reg_v & 0x1fff) >> 3)),
+				buff_ptr[(reg_v & 0x1fff) >> 3]);
+
+			buff_ptr[err_loc0 / 8] ^=
+				(1 << (reg_v & BCH_CORRECT_LOC_MASK));
+
+			printf("%x\n", buff_ptr[(reg_v & 0x1fff) >> 3]);
+
+			max_bitflips++;
+		}
+
+		if (((k + 1) * 2) <= err_num && ((err_loc1 / 8) <
+						 BCH_DATA_UNIT)) {
+			printf("pdata[%x]:%x =>", ((i / chip->ecc.bytes) *
+				chip->ecc.size + (((reg_v >> 16) & 0x1fff) >>
+				3)), buff_ptr[((reg_v >> 16) & 0x1fff) >> 3]);
+
+			buff_ptr[err_loc1 / 8] ^= (1 << ((reg_v >> 16) &
+						   BCH_CORRECT_LOC_MASK));
+
+			printf("%x\n", buff_ptr[((reg_v >> 16) & 0x1fff) >> 3]);
+
+			max_bitflips++;
+		}
+	}
+
+	return max_bitflips;
+}
+
+static int ca_do_bch_decode(struct mtd_info *mtd, struct nand_chip *chip,
+			    const u8 *buf, int page, unsigned int addr)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	unsigned int reg_v, err_num;
+	unsigned char *ecc_code = chip->buffers->ecccode;
+	unsigned char *ecc_end_pos;
+	int ret, i, j, k, n, step, eccsteps, max_bitflips = 0;
+	u8 *buff_ptr = (u8 *)buf;
+
+	for (i = 0; i < chip->ecc.total; i++)
+		ecc_code[i] = chip->oob_poi[eccoob.eccpos[i]];
+
+	for (i = 0, eccsteps = chip->ecc.steps; eccsteps;
+	     i += chip->ecc.bytes, eccsteps--) {
+		ecc_end_pos = ecc_code + chip->ecc.bytes;
+
+		for (j = 0, k = 0; j < chip->ecc.bytes; j += 4, k++) {
+			reg_v = 0;
+			for (n = 0; n < 4 && ecc_code != ecc_end_pos;
+			     ++n, ++ecc_code) {
+				reg_v |= *ecc_code << (8 * n);
+			}
+			clrsetbits_le32(&info->reg->flash_nf_bch_oob0 + k,
+					GENMASK(31, 0), reg_v);
+		}
+
+		/* Clear ECC buffer */
+		setbits_le32(&info->reg->flash_nf_ecc_reset, RESET_NFLASH_ECC);
+		ret = readl_poll_timeout(&info->reg->flash_nf_ecc_reset, reg_v,
+					 !(reg_v & RESET_NFLASH_ECC),
+					 FLASH_SHORT_DELAY);
+		if (ret)
+			pr_err("Reset ECC buffer fail\n");
+
+		clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(8, 8),
+				BCH_DISABLE);
+
+		/* Start BCH */
+		step = i / chip->ecc.bytes;
+		clrsetbits_le32(&info->reg->flash_nf_bch_control,
+				GENMASK(6, 4), step << 4);
+		setbits_le32(&info->reg->flash_nf_bch_control, BCH_ENABLE);
+		udelay(10);
+		setbits_le32(&info->reg->flash_nf_bch_control, BCH_COMPARE);
+
+		ret = readl_poll_timeout(&info->reg->flash_nf_bch_status, reg_v,
+					 (reg_v & BCH_DECO_DONE),
+					 FLASH_SHORT_DELAY);
+		if (ret)
+			pr_err("ECC Decode timeout\n");
+
+		/* Stop compare */
+		clrbits_le32(&info->reg->flash_nf_bch_control, BCH_COMPARE);
+
+		reg_v = readl(&info->reg->flash_nf_bch_status);
+		err_num = (reg_v >> 8) & BCH_ERR_NUM_MASK;
+		reg_v &= BCH_ERR_MASK;
+
+		/* Uncorrectable */
+		if (reg_v == BCH_UNCORRECTABLE) {
+			max_bitflips =
+			nand_check_erased_ecc_chunk(buff_ptr,
+						    chip->ecc.size,
+						    &chip->buffers->ecccode[i],
+						    chip->ecc.bytes,
+						    NULL, 0,
+						    chip->ecc.strength);
+
+			if (max_bitflips) {
+				mtd->ecc_stats.failed++;
+				pr_err("Uncorrectable error\n");
+				pr_err(" Page:%x  step:%d\n", page, step);
+
+				return -1;
+			}
+		} else if (reg_v == BCH_CORRECTABLE_ERR) {
+			printf("Correctable error(%x)!! addr:%lx\n",
+			       err_num, (unsigned long)addr - mtd->writesize);
+			printf("Dst buf: %p [ColSel:%x ]\n",
+			       buff_ptr + reg_v * BCH_DATA_UNIT, step);
+
+			max_bitflips =
+			   ca_do_bch_correction(chip, err_num, buff_ptr, i);
+		}
+
+		buff_ptr += BCH_DATA_UNIT;
+	}
+
+	/* Disable BCH */
+	clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(31, 0),
+			BCH_DISABLE);
+
+	return max_bitflips;
+}
+
+static int ca_do_bch_encode(struct mtd_info *mtd, struct nand_chip *chip,
+			    int page)
+{
+	struct nand_drv *info;
+	unsigned int reg_v;
+	int i, j, n, eccsteps, gen_index;
+
+	info = (struct nand_drv *)nand_get_controller_data(chip);
+
+	for (i = 0, n = 0, eccsteps = chip->ecc.steps; eccsteps;
+	     i += chip->ecc.bytes, eccsteps--, n++) {
+		gen_index = 0;
+		for (j = 0; j < chip->ecc.bytes; j += 4, gen_index++) {
+			reg_v =
+			    readl(&info->reg->flash_nf_bch_gen0_0 + gen_index +
+				  18 * n);
+			chip->oob_poi[eccoob.eccpos[i + j]] = reg_v & OOB_MASK;
+			chip->oob_poi[eccoob.eccpos[i + j + 1]] =
+			    (reg_v >> 8) & OOB_MASK;
+			chip->oob_poi[eccoob.eccpos[i + j + 2]] =
+			    (reg_v >> 16) & OOB_MASK;
+			chip->oob_poi[eccoob.eccpos[i + j + 3]] =
+			    (reg_v >> 24) & OOB_MASK;
+		}
+	}
+
+	/* Disable BCH */
+	clrsetbits_le32(&info->reg->flash_nf_bch_control, GENMASK(8, 8),
+			BCH_DISABLE);
+
+	return 0;
+}
+
+/**
+ * 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
+ *			-ETIMEDOUT when command timeout
+ */
+static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
+			const u8 *buf, int page, int with_ecc, int is_writing)
+{
+	unsigned int reg_v, ext_addr, addr, dma_index;
+	struct tx_descriptor_t *tx_desc;
+	struct rx_descriptor_t *rx_desc;
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	int ret;
+
+	/* reset ecc control */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+			RESET_NFLASH_ECC);
+
+	/*  flash interrupt */
+	clrsetbits_le32(&info->reg->flash_flash_interrupt, GENMASK(0, 0),
+			REGIRQ_CLEAR);
+
+	/* reset ecc control */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+			RESET_NFLASH_ECC);
+
+	/* Disable TXQ */
+	clrbits_le32(&info->dma_nand->dma_q_txq_control, GENMASK(0, 0));
+
+	/* Clear interrupt */
+	setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, GENMASK(0, 0));
+	setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, GENMASK(0, 0));
+
+	if (with_ecc == 1) {
+		switch (info->config.nand_ecc_strength) {
+		case ECC_STRENGTH_8:
+			reg_v = BCH_ERR_CAP_8;
+			break;
+		case ECC_STRENGTH_16:
+			reg_v = BCH_ERR_CAP_16;
+			break;
+		case ECC_STRENGTH_24:
+			reg_v = BCH_ERR_CAP_24;
+			break;
+		case ECC_STRENGTH_40:
+			reg_v = BCH_ERR_CAP_40;
+			break;
+		default:
+			reg_v = BCH_ERR_CAP_16;
+			break;
+		}
+		reg_v |= BCH_ENABLE;
+
+		/* BCH decode for flash read */
+		if (is_writing == 0)
+			reg_v |= BCH_DECODE;
+		clrsetbits_le32(&info->reg->flash_nf_bch_control,
+				GENMASK(31, 0), reg_v);
+	} else {
+		clrsetbits_le32(&info->reg->flash_nf_bch_control,
+				GENMASK(31, 0), 0);
+	}
+
+	/* Fill Extend address */
+	ext_addr = ((page << chip->page_shift) / EXT_ADDR_MASK);
+
+	clrsetbits_le32(&info->reg->flash_nf_access,
+			GENMASK(7, 0), (uintptr_t)ext_addr);
+
+	addr = (uintptr_t)((page << chip->page_shift) % EXT_ADDR_MASK);
+	addr = (uintptr_t)(addr + info->flash_base);
+
+	dma_index = readl(&info->dma_nand->dma_q_txq_wptr) & CA_DMA_Q_PTR_MASK;
+
+	tx_desc = info->tx_desc;
+	rx_desc = info->rx_desc;
+
+	/* TX/RX descriptor for page data */
+	tx_desc[dma_index].own = OWN_DMA;
+	tx_desc[dma_index].buf_len = mtd->writesize;
+	rx_desc[dma_index].own = OWN_DMA;
+	rx_desc[dma_index].buf_len = mtd->writesize;
+	if (is_writing == 0) {
+		tx_desc[dma_index].buf_adr = (uintptr_t)addr;
+		rx_desc[dma_index].buf_adr = (uintptr_t)(buf);
+	} else {
+		tx_desc[dma_index].buf_adr = (uintptr_t)buf;
+		rx_desc[dma_index].buf_adr = (uintptr_t)(addr);
+	}
+
+	dma_index++;
+	dma_index %= CA_DMA_DESC_NUM;
+
+	/* TX/RX descriptor for OOB area */
+	addr = (uintptr_t)(addr + mtd->writesize);
+	tx_desc[dma_index].own = OWN_DMA;
+	tx_desc[dma_index].buf_len = mtd->oobsize;
+	rx_desc[dma_index].own = OWN_DMA;
+	rx_desc[dma_index].buf_len = mtd->oobsize;
+	if (is_writing) {
+		tx_desc[dma_index].buf_adr = (uintptr_t)(chip->oob_poi);
+		rx_desc[dma_index].buf_adr = (uintptr_t)addr;
+	} else {
+		tx_desc[dma_index].buf_adr = (uintptr_t)addr;
+		rx_desc[dma_index].buf_adr = (uintptr_t)(chip->oob_poi);
+		dma_index++;
+		dma_index %= CA_DMA_DESC_NUM;
+	}
+
+	if (is_writing == 1) {
+		clrsetbits_le32(&info->reg->flash_fifo_control, GENMASK(1, 0),
+				FIFO_WRITE);
+	} else {
+		clrsetbits_le32(&info->reg->flash_fifo_control, GENMASK(1, 0),
+				FIFO_READ);
+	}
+
+	/* Start FIFO request */
+	clrsetbits_le32(&info->reg->flash_flash_access_start, GENMASK(2, 2),
+			NFLASH_FIFO_REQ);
+
+	/* Update DMA write pointer */
+	clrsetbits_le32(&info->dma_nand->dma_q_txq_wptr, GENMASK(12, 0),
+			dma_index);
+
+	/* Start DMA */
+	clrsetbits_le32(&info->dma_nand->dma_q_txq_control, GENMASK(0, 0),
+			TX_DMA_ENABLE);
+
+	/* Wait TX DMA done */
+	ret =
+	    readl_poll_timeout(&info->dma_nand->dma_q_txq_coal_interrupt,
+			       reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+	if (ret) {
+		pr_err("TX DMA timeout\n");
+		return -ETIMEDOUT;
+	}
+	/* clear tx interrupt */
+	setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, 1);
+
+	/* Wait RX DMA done */
+	ret =
+	    readl_poll_timeout(&info->dma_nand->dma_q_rxq_coal_interrupt, reg_v,
+			       (reg_v & 1), FLASH_LONG_DELAY);
+	if (ret) {
+		pr_err("RX DMA timeout\n");
+		return -ETIMEDOUT;
+	}
+	/* clear rx interrupt */
+	setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, 1);
+
+	/* wait NAND CMD done */
+	if (is_writing == 0) {
+		if (!nand_waitfor_cmd_completion(info->reg, NFLASH_FIFO_REQ))
+			printf("%s: Command timeout\n", __func__);
+	}
+
+	/* Update DMA read pointer */
+	clrsetbits_le32(&info->dma_nand->dma_q_rxq_rptr, GENMASK(12, 0),
+			dma_index);
+
+	/* ECC correction */
+	if (with_ecc == 1) {
+		ret =
+		    readl_poll_timeout(&info->reg->flash_nf_bch_status,
+				       reg_v, (reg_v & BCH_GEN_DONE),
+				       FLASH_LONG_DELAY);
+
+		if (ret) {
+			pr_err("BCH_GEN timeout! flash_nf_bch_status=[0x%x]\n",
+			       reg_v);
+			return -ETIMEDOUT;
+		}
+
+		if (is_writing == 0)
+			ca_do_bch_decode(mtd, chip, buf, page, addr);
+		else
+			ca_do_bch_encode(mtd, chip, page);
+	}
+
+	if (is_writing) {
+		dma_index++;
+		dma_index %= CA_DMA_DESC_NUM;
+
+		/* Update DMA R/W pointer */
+		clrsetbits_le32(&info->dma_nand->dma_q_txq_wptr, GENMASK(12, 0),
+				dma_index);
+
+		/* Wait TX DMA done */
+		ret =
+		   readl_poll_timeout(&info->dma_nand->dma_q_txq_coal_interrupt,
+				      reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+		if (ret) {
+			pr_err("TX DMA timeout\n");
+			return -ETIMEDOUT;
+		}
+		/* clear tx interrupt */
+		setbits_le32(&info->dma_nand->dma_q_txq_coal_interrupt, 1);
+
+		/* Wait RX DMA done */
+		ret =
+		   readl_poll_timeout(&info->dma_nand->dma_q_rxq_coal_interrupt,
+				      reg_v, (reg_v & 1), FLASH_LONG_DELAY);
+		if (ret) {
+			pr_err("RX DMA timeout\n");
+			return -ETIMEDOUT;
+		}
+		/* clear rx interrupt */
+		setbits_le32(&info->dma_nand->dma_q_rxq_coal_interrupt, 1);
+
+		/* wait NAND CMD done */
+		if (!nand_waitfor_cmd_completion(info->reg, NFLASH_FIFO_REQ))
+			printf("%s: Command timeout\n", __func__);
+
+		/* Update DMA R/W pointer */
+		clrsetbits_le32(&info->dma_nand->dma_q_rxq_rptr, GENMASK(12, 0),
+				dma_index);
+	}
+
+	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
+ *		-ETIMEDOUT when command timeout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd,
+				struct nand_chip *chip, uint8_t *buf,
+				int oob_required, int page)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	int ret;
+
+	ret = nand_rw_page(mtd, chip, buf, page, 1, 0);
+	if (ret)
+		return ret;
+
+	/* Reset FIFO */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+			ECC_RESET_ALL);
+
+	return 0;
+}
+
+/**
+ * Hardware ecc based page write function
+ *
+ * @param mtd	mtd info structure
+ * @param chip	nand chip info structure
+ * @param buf	data buffer
+ * @return	0 when successfully completed
+ *		-ETIMEDOUT when command timeout
+ */
+static int nand_write_page_hwecc(struct mtd_info *mtd,
+				 struct nand_chip *chip, const uint8_t *buf,
+				 int oob_required, int page)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	int ret;
+
+	ret = nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1);
+	if (ret)
+		return ret;
+
+	/* Reset FIFO */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+			ECC_RESET_ALL);
+
+	return 0;
+}
+
+/**
+ * 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
+ *		-ETIMEDOUT when command timeout
+ */
+static int nand_read_page_raw(struct mtd_info *mtd,
+			      struct nand_chip *chip, uint8_t *buf,
+			      int oob_required, int page)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	int ret;
+
+	ret = nand_rw_page(mtd, chip, buf, page, 0, 0);
+	if (ret)
+		return ret;
+
+	/* Reset FIFO */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+			ECC_RESET_ALL);
+
+	return 0;
+}
+
+/**
+ * Raw page write function
+ *
+ * @param mtd	mtd info structure
+ * @param chip	nand chip info structure
+ * @param buf	data buffer
+ * @return	0 when successfully completed
+ *		-ETIMEDOUT when command timeout
+ */
+static int nand_write_page_raw(struct mtd_info *mtd,
+			       struct nand_chip *chip, const uint8_t *buf,
+			       int oob_required, int page)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	int ret;
+
+	ret = nand_rw_page(mtd, chip, buf, page, 0, 1);
+	if (ret)
+		return ret;
+
+	/* Reset FIFO */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset, GENMASK(31, 0),
+			ECC_RESET_ALL);
+
+	return 0;
+}
+
+/**
+ * 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
+ *			-ETIMEDOUT when command timeout
+ */
+static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
+		       int page, int with_ecc, int is_writing)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	u32 reg_val;
+	int rw_index;
+
+	if (is_writing) {
+		reg_val = NFLASH_GO | NFLASH_WT;
+		pwrite = (unsigned int *)chip->oob_poi;
+	} else {
+		reg_val = NFLASH_GO | NFLASH_RD;
+		pread = (unsigned int *)chip->oob_poi;
+	}
+
+	for (rw_index = 0; rw_index < mtd->oobsize / 4; rw_index++) {
+		clrsetbits_le32(&info->reg->flash_nf_access, GENMASK(31, 0),
+				NFLASH_REG_WIDTH_32);
+		if (is_writing)
+			clrsetbits_le32(&info->reg->flash_nf_data,
+					GENMASK(31, 0), pwrite[rw_index]);
+
+		clrsetbits_le32(&info->reg->flash_flash_access_start,
+				GENMASK(11, 10), reg_val);
+
+		if (!nand_waitfor_cmd_completion(info->reg, NFLASH_GO))
+			printf("%s: Command timeout\n", __func__);
+
+		if (!is_writing)
+			pread[rw_index] = readl(&info->reg->flash_nf_data);
+	}
+	return 0;
+}
+
+/**
+ * OOB data read function
+ *
+ * @param mtd		mtd info structure
+ * @param chip		nand chip info structure
+ * @param page		page number to read
+ */
+static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	int ret;
+
+	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+	if (mtd->writesize <= (REG_DATA_COUNT_512_DATA >> 8))
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(7, 0),
+				NAND_CMD_READOOB);
+	ret = nand_rw_oob(mtd, chip, page, 0, 0);
+
+	/* Reset FIFO */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset,
+			GENMASK(31, 0), ECC_RESET_ALL);
+
+	return ret;
+}
+
+/**
+ * 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
+ *		-ETIMEDOUT when command timeout
+ */
+static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			  int page)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(chip);
+	int ret;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+	if (mtd->writesize <= (REG_DATA_COUNT_512_DATA >> 8)) {
+		clrsetbits_le32(&info->reg->flash_nf_command, GENMASK(31, 0),
+				NAND_CMD_READOOB | (NAND_CMD_SEQIN << 8) |
+				(NAND_CMD_PAGEPROG << 16));
+		clrsetbits_le32(&info->reg->flash_nf_count, GENMASK(1, 0),
+				REG_CMD_COUNT_3TOGO);
+	}
+	ret = nand_rw_oob(mtd, chip, page, 1, 1);
+
+	/* Reset FIFO */
+	clrsetbits_le32(&info->reg->flash_nf_ecc_reset,
+			GENMASK(31, 0), ECC_RESET_ALL);
+
+	return ret;
+}
+
+/**
+ * Decode NAND parameters from the device tree
+ *
+ * @param dev		Driver model device
+ * @param config	Device tree NAND configuration
+ */
+static int fdt_decode_nand(struct udevice *dev, struct nand_drv *info)
+{
+	int ecc_strength;
+
+	info->reg = (struct nand_ctlr *)dev_read_addr(dev);
+	info->dma_glb = (struct dma_global *)dev_read_addr_index(dev, 1);
+	info->dma_nand = (struct dma_ssp *)dev_read_addr_index(dev, 2);
+	info->config.enabled = dev_read_enabled(dev);
+	ecc_strength = dev_read_u32_default(dev, "nand-ecc-strength", 16);
+	info->flash_base =
+	    dev_read_u32_default(dev, "nand_flash_base_addr", NAND_BASE_ADDR);
+
+	switch (ecc_strength) {
+	case ECC_STRENGTH_8:
+		info->config.nand_ecc_strength = ECC_STRENGTH_8;
+		break;
+	case ECC_STRENGTH_16:
+		info->config.nand_ecc_strength = ECC_STRENGTH_16;
+		break;
+	case ECC_STRENGTH_24:
+		info->config.nand_ecc_strength = ECC_STRENGTH_24;
+		break;
+	case ECC_STRENGTH_40:
+		info->config.nand_ecc_strength = ECC_STRENGTH_40;
+		break;
+	default:
+		info->config.nand_ecc_strength = ECC_STRENGTH_16;
+	}
+
+	return 0;
+}
+
+/**
+ * config flash type
+ *
+ * @param chip	nand chip info structure
+ */
+static void nand_config_flash_type(struct nand_chip *nand)
+{
+	struct nand_drv *info =
+	    (struct nand_drv *)nand_get_controller_data(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+
+	switch (mtd->writesize) {
+	case WRITE_SIZE_512:
+		clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+				FLASH_PIN | FLASH_TYPE_512);
+		break;
+	case WRITE_SIZE_2048:
+		clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+				FLASH_PIN | FLASH_TYPE_2K);
+		break;
+	case WRITE_SIZE_4096:
+		clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+				FLASH_PIN | FLASH_TYPE_4K);
+		break;
+	case WRITE_SIZE_8192:
+		clrsetbits_le32(&info->reg->flash_type, GENMASK(31, 0),
+				FLASH_PIN | FLASH_TYPE_8K);
+		break;
+	default:
+		pr_err("Unsupported page size(0x%x)!", nand->ecc.size);
+	}
+}
+
+/**
+ * config oob layout
+ *
+ * @param chip  nand chip info structure
+ * @return	0 when successfully completed
+ *		-EINVAL when ECC bytes exceed OOB size
+ */
+static int nand_config_oob_layout(struct nand_chip *nand)
+{
+	int i, ecc_start_offset;
+	struct mtd_info *mtd = nand_to_mtd(nand);
+
+	/* Calculate byte count for ECC */
+	eccoob.eccbytes = mtd->writesize / nand->ecc.size * nand->ecc.bytes;
+
+	if (mtd->oobsize < eccoob.eccbytes) {
+		pr_err("Spare area(%d) too small for BCH%d\n", nand->ecc.bytes,
+		       nand->ecc.strength / 8);
+		pr_err("page_sz: %d\n", nand->ecc.size);
+		pr_err("oob_sz: %d\n", nand->ecc.bytes);
+		return -EINVAL;
+	}
+
+	/* Update OOB layout */
+	ecc_start_offset = mtd->oobsize - eccoob.eccbytes;
+	memset(eccoob.eccpos, 0, sizeof(eccoob.eccpos));
+	for (i = 0; i < eccoob.eccbytes; ++i)
+		eccoob.eccpos[i] = i + ecc_start_offset;
+
+	/* Unused spare area
+	 * OOB[0] is bad block marker.
+	 * Extra two byte is reserved as
+	 * erase marker just right before ECC code.
+	 */
+	eccoob.oobavail = nand->ecc.bytes - eccoob.eccbytes - 2;
+	eccoob.oobfree[0].offset = 2;
+	eccoob.oobfree[0].length =
+	    mtd->oobsize - eccoob.eccbytes - eccoob.oobfree[0].offset - 1;
+
+	return 0;
+}
+
+static int ca_nand_probe(struct udevice *dev)
+{
+	struct ca_nand_info *ca_nand = dev_get_priv(dev);
+	struct nand_chip *nand = &ca_nand->nand_chip;
+	struct nand_drv *info = &ca_nand->nand_ctrl;
+	struct fdt_nand *config = &info->config;
+	struct mtd_info *our_mtd;
+	int ret;
+
+	if (fdt_decode_nand(dev, info)) {
+		printf("Could not decode nand-flash in device tree\n");
+		return -1;
+	}
+	if (!config->enabled)
+		return -1;
+
+	nand->ecc.mode = NAND_ECC_HW;
+	nand->ecc.layout = &eccoob;
+
+	nand->cmdfunc = ca_nand_command;
+	nand->read_byte = read_byte;
+	nand->read_buf = read_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->ecc.strength = config->nand_ecc_strength;
+	nand->select_chip = nand_select_chip;
+	nand->dev_ready = nand_dev_ready;
+	nand_set_controller_data(nand, &ca_nand->nand_ctrl);
+
+	/* Disable subpage writes as we do not provide ecc->hwctl */
+	nand->options |= NAND_NO_SUBPAGE_WRITE | NAND_SKIP_BBTSCAN;
+
+	/* Configure flash type as P-NAND */
+	clrsetbits_le32(&info->reg->flash_type, FLASH_PIN,
+			FLASH_TYPE_4K | FLASH_SIZE_436OOB);
+	config->width = FLASH_WIDTH;
+
+	our_mtd = nand_to_mtd(nand);
+	ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
+	if (ret)
+		return ret;
+
+	nand->ecc.size = BCH_DATA_UNIT;
+	nand->ecc.bytes = BCH_GF_PARAM_M * (nand->ecc.strength / 8);
+
+	/* Reconfig flash type according to ONFI */
+	nand_config_flash_type(nand);
+
+	ret = set_bus_width_page_size(our_mtd);
+	if (ret)
+		return ret;
+
+	/* Set the bad block position */
+	nand->badblockpos =
+	    our_mtd->writesize >
+	    512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+
+	/* Arrange OOB layout */
+	ret = nand_config_oob_layout(nand);
+	if (ret)
+		return ret;
+
+	/* Init DMA descriptor ring */
+	ret = init_nand_dma(nand);
+	if (ret)
+		return ret;
+
+	ret = nand_scan_tail(our_mtd);
+	if (ret)
+		return ret;
+
+	ret = nand_register(0, our_mtd);
+	if (ret) {
+		dev_err(dev, "Failed to register MTD: %d\n", ret);
+		return ret;
+	}
+
+	ret = set_bus_width_page_size(our_mtd);
+	if (ret)
+		return ret;
+
+	printf("P-NAND    : %s\n", our_mtd->name);
+	printf("Chip  Size: %lldMB\n", nand->chipsize / (1024 * 1024));
+	printf("Block Size: %dKB\n", our_mtd->erasesize / 1024);
+	printf("Page  Size: %dB\n", our_mtd->writesize);
+	printf("OOB   Size: %dB\n", our_mtd->oobsize);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(cortina_nand) = {
+	.name = "CA-PNAND",
+	.id = UCLASS_MTD,
+	.of_match = cortina_nand_dt_ids,
+	.probe = ca_nand_probe,
+	.priv_auto_alloc_size = sizeof(struct ca_nand_info),
+};
+
+void board_nand_init(void)
+{
+	struct udevice *dev;
+	int ret;
+
+	ret = uclass_get_device_by_driver(UCLASS_MTD,
+					  DM_GET_DRIVER(cortina_nand), &dev);
+	if (ret && ret != -ENODEV)
+		pr_err("Failed to initialize %s. (error %d)\n", dev->name, ret);
+}
diff --git a/drivers/mtd/nand/raw/cortina_nand.h b/drivers/mtd/nand/raw/cortina_nand.h
new file mode 100644
index 0000000..1c756d2
--- /dev/null
+++ b/drivers/mtd/nand/raw/cortina_nand.h
@@ -0,0 +1,293 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2020 Cortina Access Inc..
+ */
+
+/* Cortina NAND definition */
+#define NAND_BASE_ADDR		0xE0000000
+#define BCH_GF_PARAM_M		14
+#define BCH_DATA_UNIT		1024
+#define FLASH_SHORT_DELAY	100
+#define FLASH_LONG_DELAY	1000
+#define FLASH_WIDTH		16
+#define BBT_PAGE_MASK		0xffffff3f
+#define WRITE_SIZE_512		512
+#define WRITE_SIZE_2048		2048
+#define WRITE_SIZE_4096		4096
+#define WRITE_SIZE_8192		8192
+#define ECC_STRENGTH_8		8
+#define ECC_STRENGTH_16		16
+#define ECC_STRENGTH_24		24
+#define ECC_STRENGTH_40		40
+#define EMPTY_PAGE		0xff
+#define ADDR1_MASK0		0x00ffffff
+#define ADDR2_MASK0		0xff000000
+#define ADDR1_MASK1		0xffff
+#define ADDR1_MASK2		0xff
+#define OOB_MASK		0xff
+#define EXT_ADDR_MASK		0x8000000
+
+/* Status bits */
+#define NAND_STATUS_FAIL	0x01
+#define NAND_STATUS_FAIL_N1	0x02
+#define NAND_STATUS_TRUE_READY	0x20
+#define NAND_STATUS_READY	0x40
+#define NAND_STATUS_WP		0x80
+
+/* Bit field in FLAS_TYPE */
+#define FLASH_PIN			BIT(15)
+#define FLASH_TYPE_512			0x4000
+#define FLASH_TYPE_2K			0x5000
+#define FLASH_TYPE_4K			0x6000
+#define FLASH_TYPE_8K			0x7000
+#define FLASH_SIZE_CONFIGURABLEOOB	(0x0 << 9)
+#define FLASH_SIZE_400OOB		(0x1 << 9)
+#define FLASH_SIZE_436OOB		(0x2 << 9)
+#define FLASH_SIZE_640OOB		(0x3 << 9)
+
+/* Bit field in FLASH_STATUS */
+#define NFLASH_READY	BIT(26)
+
+/* Bit field in FLASH_NF_ACCESS */
+#define NFLASH_ENABLE_ALTERNATIVE	(0x0 << 15)
+#define AUTO_RESET			BIT(16)
+#define DISABLE_AUTO_RESET		(0x0 << 16)
+#define NFLASH_REG_WIDTH_RESERVED	(0x3 << 10)
+#define NFLASH_REG_WIDTH_32		(0x2 << 10)
+#define NFLASH_REG_WIDTH_16		(0x1 << 10)
+#define NFLASH_REG_WIDTH_8		(0x0 << 10)
+
+/* Bit field in FLASH_NF_COUNT */
+#define REG_CMD_COUNT_EMPTY		0x3
+#define REG_CMD_COUNT_3TOGO		0x2
+#define REG_CMD_COUNT_2TOGO		0x1
+#define REG_CMD_COUNT_1TOGO		0x0
+#define REG_ADDR_COUNT_EMPTY		(0x7 << 4)
+#define REG_ADDR_COUNT_5		(0x4 << 4)
+#define REG_ADDR_COUNT_4		(0x3 << 4)
+#define REG_ADDR_COUNT_3		(0x2 << 4)
+#define REG_ADDR_COUNT_2		(0x1 << 4)
+#define REG_ADDR_COUNT_1		(0x0 << 4)
+#define REG_DATA_COUNT_EMPTY		(0x3fff << 8)
+#define REG_DATA_COUNT_512_DATA		(0x1FF << 8)
+#define REG_DATA_COUNT_2k_DATA		(0x7FF << 8)
+#define REG_DATA_COUNT_4k_DATA		(0xFFF << 8)
+#define REG_DATA_COUNT_DATA_1		(0x0 << 8)
+#define REG_DATA_COUNT_DATA_2		(0x1 << 8)
+#define REG_DATA_COUNT_DATA_3		(0x2 << 8)
+#define REG_DATA_COUNT_DATA_4		(0x3 << 8)
+#define REG_DATA_COUNT_DATA_5		(0x4 << 8)
+#define REG_DATA_COUNT_DATA_6		(0x5 << 8)
+#define REG_DATA_COUNT_DATA_7		(0x6 << 8)
+#define REG_DATA_COUNT_DATA_8		(0x7 << 8)
+#define REG_OOB_COUNT_EMPTY		(0x3ff << 22)
+
+/* Bit field in FLASH_FLASH_ACCESS_START */
+#define NFLASH_GO		BIT(0)
+#define NFLASH_FIFO_REQ		BIT(2)
+#define NFLASH_RD		BIT(13)
+#define NFLASH_WT		(BIT(12) | BIT(13))
+
+/* Bit field in FLASH_NF_ECC_RESET */
+#define RESET_NFLASH_RESET	BIT(2)
+#define RESET_NFLASH_FIFO	BIT(1)
+#define RESET_NFLASH_ECC	BIT(0)
+#define ECC_RESET_ALL \
+	RESET_NFLASH_RESET | RESET_NFLASH_FIFO | RESET_NFLASH_ECC
+
+/* Bit field in FLASH_NF_ECC_CONTROL */
+#define ENABLE_ECC_GENERATION	BIT(8)
+#define DISABLE_ECC_GENERATION	(0 << 8)
+
+/* Flash FIFO control */
+#define FIFO_READ		2
+#define FIFO_WRITE		3
+
+/* NFLASH INTERRUPT */
+#define REGIRQ_CLEAR		BIT(0)
+#define F_ADDR_ERR		2
+
+/* BCH ECC field definition */
+#define BCH_COMPARE		BIT(0)
+#define	BCH_ENABLE		BIT(8)
+#define	BCH_DISABLE		(0 << 8)
+#define	BCH_DECODE		BIT(1)
+#define	BCH_ENCODE		(0 << 1)
+#define BCH_DECO_DONE		BIT(30)
+#define BCH_GEN_DONE		BIT(31)
+#define	BCH_UNCORRECTABLE	0x3
+#define	BCH_CORRECTABLE_ERR	0x2
+#define	BCH_NO_ERR		0x1
+#define	BCH_BUSY		0x0
+#define BCH_ERR_MASK		0x3
+#define BCH_ERR_NUM_MASK	0x3F
+#define BCH_ERR_LOC_MASK	0x3FFF
+#define BCH_CORRECT_LOC_MASK	0x7
+#define BCH_ERR_CAP_8		(0x0 << 9)
+#define BCH_ERR_CAP_16		(0x1 << 9)
+#define BCH_ERR_CAP_24		(0x2 << 9)
+#define BCH_ERR_CAP_40		(0x3 << 9)
+
+#define BCH_GF_PARAM_M		14
+
+struct nand_ctlr {
+	/* Cortina NAND controller register */
+	u32 flash_id;		     // offset 0x000
+	u32 flash_timeout;		// offset 0x004
+	u32 flash_status;		 // offset 0x008
+	u32 flash_type;		   // offset 0x00c
+	u32 flash_flash_access_start;     // offset 0x010
+	u32 flash_flash_interrupt;	// offset 0x014
+	u32 flash_flash_mask;	     // offset 0x018
+	u32 flash_fifo_control;	   // offset 0x01c
+	u32 flash_fifo_status;	    // offset 0x020
+	u32 flash_fifo_address;	   // offset 0x024
+	u32 flash_fifo_match_address;     // offset 0x028
+	u32 flash_fifo_data;	      // offset 0x02c
+	u32 flash_sf_access;	      // offset 0x030
+	u32 flash_sf_ext_access;	  // offset 0x034
+	u32 flash_sf_address;	     // offset 0x038
+	u32 flash_sf_data;		// offset 0x03c
+	u32 flash_sf_timing;	      // offset 0x040
+	u32 resv[3];
+	u32 flash_pf_access;	      // offset 0x050
+	u32 flash_pf_timing;	      // offset 0x054
+	u32 resv1[2];
+	u32 flash_nf_access;	      // offset 0x060
+	u32 flash_nf_count;	       // offset 0x064
+	u32 flash_nf_command;	     // offset 0x068
+	u32 flash_nf_address_1;	   // offset 0x06c
+	u32 flash_nf_address_2;	   // offset 0x070
+	u32 flash_nf_data;		// offset 0x074
+	u32 flash_nf_timing;	      // offset 0x078
+	u32 flash_nf_ecc_status;	  // offset 0x07c
+	u32 flash_nf_ecc_control;	 // offset 0x080
+	u32 flash_nf_ecc_oob;	     // offset 0x084
+	u32 flash_nf_ecc_gen0;	    // offset 0x088
+	u32 resv3[15];
+	u32 flash_nf_ecc_reset;	   // offset 0x0c8
+	u32 flash_nf_bch_control;	 // offset 0x0cc
+	u32 flash_nf_bch_status;	  // offset 0x0d0
+	u32 flash_nf_bch_error_loc01;     // offset 0x0d4
+	u32 resv4[19];
+	u32 flash_nf_bch_oob0;	    // offset 0x124
+	u32 resv5[17];
+	u32 flash_nf_bch_gen0_0;	  // offset 0x16c
+};
+
+/* Definition for DMA bitfield */
+#define TX_DMA_ENABLE	BIT(0)
+#define RX_DMA_ENABLE	BIT(0)
+#define DMA_CHECK_OWNER	BIT(1)
+#define OWN_DMA			0
+#define OWN_CPU			1
+
+#define CA_DMA_DEPTH	3
+#define CA_DMA_DESC_NUM	(BIT(0) << CA_DMA_DEPTH)
+#define CA_DMA_Q_PTR_MASK	0x1fff
+
+struct dma_q_base_depth_t {
+	u32 depth		:  4 ; /* bits 3:0 */
+	u32 base		: 28 ; /* bits 31:4 */
+};
+
+struct tx_descriptor_t {
+	unsigned int buf_adr; /* Buff addr */
+	unsigned int buf_adr_hi	:  8 ; /* bits 7:0 */
+	unsigned int buf_len	:  16 ;  /* bits 23:8 */
+	unsigned int sgm	:  1 ;  /* bits 24 */
+	unsigned int rsrvd	:  6 ;  /* bits 30:25 */
+	unsigned int own	:  1 ;  /* bits 31:31 */
+};
+
+struct rx_descriptor_t {
+	unsigned int buf_adr; /* Buff addr */
+	unsigned int buf_adr_hi	:  8 ; /* bits 7:0 */
+	unsigned int buf_len	: 16 ;  /* bits 23:8 */
+	unsigned int rsrvd	:  7 ;  /* bits 30:24 */
+	unsigned int own	:  1 ;  /* bits 31:31 */
+};
+
+struct dma_global {
+	u32 dma_glb_dma_lso_ctrl;
+	u32 dma_glb_lso_interrupt;
+	u32 dma_glb_lso_intenable;
+	u32 dma_glb_dma_lso_vlan_tag_type0;
+	u32 dma_glb_dma_lso_vlan_tag_type1;
+	u32 dma_glb_dma_lso_axi_user_sel0;
+	u32 dma_glb_axi_user_pat0;
+	u32 dma_glb_axi_user_pat1;
+	u32 dma_glb_axi_user_pat2;
+	u32 dma_glb_axi_user_pat3;
+	u32 dma_glb_fast_reg_pe0;
+	u32 dma_glb_fast_reg_pe1;
+	u32 dma_glb_dma_lso_tx_fdes_addr0;
+	u32 dma_glb_dma_lso_tx_fdes_addr1;
+	u32 dma_glb_dma_lso_tx_cdes_addr0;
+	u32 dma_glb_dma_lso_tx_cdes_addr1;
+	u32 dma_glb_dma_lso_tx_des_word0;
+	u32 dma_glb_dma_lso_tx_des_word1;
+	u32 dma_glb_dma_lso_lso_para_word0;
+	u32 dma_glb_dma_lso_lso_para_word1;
+	u32 dma_glb_dma_lso_debug0;
+	u32 dma_glb_dma_lso_debug1;
+	u32 dma_glb_dma_lso_debug2;
+	u32 dma_glb_dma_lso_spare0;
+	u32 dma_glb_dma_lso_spare1;
+	u32 dma_glb_dma_ssp_rx_ctrl;
+	u32 dma_glb_dma_ssp_tx_ctrl;
+	u32 dma_glb_dma_ssp_axi_user_sel0;
+	u32 dma_glb_dma_ssp_axi_user_sel1;
+	u32 dma_glb_dma_ssp_rx_fdes_addr0;
+	u32 dma_glb_dma_ssp_rx_fdes_addr1;
+	u32 dma_glb_dma_ssp_rx_cdes_addr0;
+	u32 dma_glb_dma_ssp_rx_cdes_addr1;
+	u32 dma_glb_dma_ssp_rx_des_word0;
+	u32 dma_glb_dma_ssp_rx_des_word1;
+	u32 dma_glb_dma_ssp_tx_fdes_addr0;
+	u32 dma_glb_dma_ssp_tx_fdes_addr1;
+	u32 dma_glb_dma_ssp_tx_cdes_addr0;
+	u32 dma_glb_dma_ssp_tx_cdes_addr1;
+	u32 dma_glb_dma_ssp_tx_des_word0;
+	u32 dma_glb_dma_ssp_tx_des_word1;
+	u32 dma_glb_dma_ssp_debug0;
+	u32 dma_glb_dma_ssp_debug1;
+	u32 dma_glb_dma_ssp_debug2;
+	u32 dma_glb_dma_ssp_spare0;
+	u32 dma_glb_dma_ssp_spare1;
+};
+
+struct dma_ssp {
+	u32 dma_q_rxq_control;
+	u32 dma_q_rxq_base_depth;
+	u32 dma_q_rxq_base;
+	u32 dma_q_rxq_wptr;
+	u32 dma_q_rxq_rptr;
+	u32 dma_q_rxq_pktcnt;
+	u32 dma_q_txq_control;
+	u32 dma_q_txq_base_depth;
+	u32 dma_q_txq_base;
+	u32 dma_q_txq_wptr;
+	u32 dma_q_txq_rptr;
+	u32 dma_q_txq_pktcnt;
+	u32 dma_q_rxq_interrupt;
+	u32 dma_q_rxq_intenable;
+	u32 dma_q_txq_interrupt;
+	u32 dma_q_txq_intenable;
+	u32 dma_q_rxq_misc_interrupt;
+	u32 dma_q_rxq_misc_intenable;
+	u32 dma_q_txq_misc_interrupt;
+	u32 dma_q_txq_misc_intenable;
+	u32 dma_q_rxq_coal_interrupt;
+	u32 dma_q_rxq_coal_intenable;
+	u32 dma_q_txq_coal_interrupt;
+	u32 dma_q_txq_coal_intenable;
+	u32 dma_q_rxq_frag_buff_addr0;
+	u32 dma_q_rxq_frag_buff_addr1;
+	u32 dma_q_rxq_frag_buff_size;
+	u32 dma_q_txq_frag_buff_addr0;
+	u32 dma_q_txq_frag_buff_addr1;
+	u32 dma_q_txq_frag_buff_size;
+	u32 dma_q_dma_spare_0;
+	u32 dma_q_dma_spare_1;
+};
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] board: presidio: Add Parallel NAND support
  2020-12-08 19:37 [PATCH 1/2] mtd: rawnand: cortina_nand: Add Cortina CAxxxx SoC support Alex Nemirovsky
@ 2020-12-08 19:37 ` Alex Nemirovsky
  2020-12-11 20:54   ` Tom Rini
  2020-12-11 20:54 ` [PATCH 1/2] mtd: rawnand: cortina_nand: Add Cortina CAxxxx SoC support Tom Rini
  1 sibling, 1 reply; 5+ messages in thread
From: Alex Nemirovsky @ 2020-12-08 19:37 UTC (permalink / raw)
  To: u-boot

From: Kate Liu <kate.liu@cortina-access.com>

Set environment for Nand flash (U-boot 2020.04):
- add nand flash in the device tree
- add new default configuration file for G3 using parallel Nand
- set nand parameters in presidio_asic.h

Signed-off-by: Kate Liu <kate.liu@cortina-access.com>
Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
CC: Tom Rini <trini@konsulko.com>
---

 MAINTAINERS                                   |  1 +
 arch/arm/dts/ca-presidio-engboard.dts         | 14 +++++++++++
 configs/cortina_presidio-asic-pnand_defconfig | 34 +++++++++++++++++++++++++++
 include/configs/presidio_asic.h               |  9 +++++++
 4 files changed, 58 insertions(+)
 create mode 100644 configs/cortina_presidio-asic-pnand_defconfig

diff --git a/MAINTAINERS b/MAINTAINERS
index a002263..a4d5f30 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -199,6 +199,7 @@ F:	drivers/i2c/i2c-cortina.c
 F:	drivers/i2c/i2c-cortina.h
 F:	drivers/mtd/nand/raw/cortina_nand.c
 F:	drivers/mtd/nand/raw/cortina_nand.h
+F:	configs/cortina_presidio-asic-pnand_defconfig
 
 ARM/CZ.NIC TURRIS MOX SUPPORT
 M:	Marek Behun <marek.behun@nic.cz>
diff --git a/arch/arm/dts/ca-presidio-engboard.dts b/arch/arm/dts/ca-presidio-engboard.dts
index eef433e..0ab52fd 100644
--- a/arch/arm/dts/ca-presidio-engboard.dts
+++ b/arch/arm/dts/ca-presidio-engboard.dts
@@ -52,6 +52,20 @@
 		clock-frequency = <400000>;
 	};
 
+	nand: nand-controller at f4324000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "cortina,ca-nand";
+		reg = <0 0xf4324000 0x3b0>, /* NAND controller */
+		      <0 0xf7001000 0xb4>, /* DMA_GLOBAL */
+		      <0 0xf7001a00 0x80>; /* DMA channel0 for FLASH */
+		status = "okay";
+		nand-ecc-mode = "hw";
+		nand-ecc-strength = <16>;
+		nand-ecc-step-size = <1024>;    /* Must be 1024 */
+		nand_flash_base_addr = <0xe0000000>;
+	};
+
 	sflash: sflash-controller at f4324000 {
 		#address-cells = <2>;
 		#size-cells = <1>;
diff --git a/configs/cortina_presidio-asic-pnand_defconfig b/configs/cortina_presidio-asic-pnand_defconfig
new file mode 100644
index 0000000..ed83da1
--- /dev/null
+++ b/configs/cortina_presidio-asic-pnand_defconfig
@@ -0,0 +1,34 @@
+CONFIG_ARM=y
+# CONFIG_SYS_ARCH_TIMER is not set
+CONFIG_TARGET_PRESIDIO_ASIC=y
+CONFIG_SYS_TEXT_BASE=0x04000000
+CONFIG_ENV_SIZE=0x20000
+CONFIG_DM_GPIO=y
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_IDENT_STRING="Presidio-SoC"
+CONFIG_SHOW_BOOT_PROGRESS=y
+CONFIG_BOOTDELAY=3
+CONFIG_USE_BOOTARGS=y
+CONFIG_BOOTARGS="earlycon=serial,0xf4329148 console=ttyS0,115200 root=/dev/ram0"
+CONFIG_BOARD_EARLY_INIT_R=y
+CONFIG_SYS_PROMPT="G3#"
+CONFIG_CMD_WDT=y
+CONFIG_CMD_CACHE=y
+CONFIG_CMD_TIMER=y
+CONFIG_CMD_SMC=y
+CONFIG_OF_CONTROL=y
+CONFIG_OF_LIVE=y
+CONFIG_DEFAULT_DEVICE_TREE="ca-presidio-engboard"
+# CONFIG_NET is not set
+CONFIG_DM=y
+CONFIG_CORTINA_GPIO=y
+# CONFIG_MMC is not set
+CONFIG_DM_SERIAL=y
+CONFIG_CORTINA_UART=y
+CONFIG_WDT=y
+CONFIG_WDT_CORTINA=y
+
+CONFIG_CMD_MTD=y
+CONFIG_MTD=y
+CONFIG_MTD_RAW_NAND=y
+CONFIG_CORTINA_NAND=y
diff --git a/include/configs/presidio_asic.h b/include/configs/presidio_asic.h
index 34235b5..710731e 100644
--- a/include/configs/presidio_asic.h
+++ b/include/configs/presidio_asic.h
@@ -67,4 +67,13 @@
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_EXTRA_ENV_SETTINGS	"silent=y\0"
 
+/* nand driver parameters */
+#ifdef CONFIG_TARGET_PRESIDIO_ASIC
+	#define CONFIG_SYS_NAND_ONFI_DETECTION
+	#define CONFIG_SYS_MAX_NAND_DEVICE      1
+	#define CONFIG_SYS_NAND_MAX_CHIPS       1
+	#define CONFIG_SYS_NAND_BASE            CONFIG_SYS_FLASH_BASE
+	#define CONFIG_SYS_NAND_BASE_LIST       { CONFIG_SYS_NAND_BASE }
+#endif
+
 #endif /* __PRESIDIO_ASIC_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 1/2] mtd: rawnand: cortina_nand: Add Cortina CAxxxx SoC support
  2020-12-08 19:37 [PATCH 1/2] mtd: rawnand: cortina_nand: Add Cortina CAxxxx SoC support Alex Nemirovsky
  2020-12-08 19:37 ` [PATCH 2/2] board: presidio: Add Parallel NAND support Alex Nemirovsky
@ 2020-12-11 20:54 ` Tom Rini
  1 sibling, 0 replies; 5+ messages in thread
From: Tom Rini @ 2020-12-11 20:54 UTC (permalink / raw)
  To: u-boot

On Tue, Dec 08, 2020 at 11:37:36AM -0800, Alex Nemirovsky wrote:

> From: Kate Liu <kate.liu@cortina-access.com>
> 
> Add Cortina Access parallel Nand support for CAxxxx SOCs
> 
> Signed-off-by: Kate Liu <kate.liu@cortina-access.com>
> Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
> CC: Tom Rini <trini@konsulko.com>
> CC: Scott Wood <oss@buserror.net>

A one small things:

[snip]
> +struct nand_ctlr {
> +	/* Cortina NAND controller register */
> +	u32 flash_id;		     // offset 0x000
> +	u32 flash_timeout;		// offset 0x004
> +	u32 flash_status;		 // offset 0x008
> +	u32 flash_type;		   // offset 0x00c
> +	u32 flash_flash_access_start;     // offset 0x010
> +	u32 flash_flash_interrupt;	// offset 0x014
> +	u32 flash_flash_mask;	     // offset 0x018
> +	u32 flash_fifo_control;	   // offset 0x01c
> +	u32 flash_fifo_status;	    // offset 0x020
> +	u32 flash_fifo_address;	   // offset 0x024
> +	u32 flash_fifo_match_address;     // offset 0x028
> +	u32 flash_fifo_data;	      // offset 0x02c
> +	u32 flash_sf_access;	      // offset 0x030
> +	u32 flash_sf_ext_access;	  // offset 0x034
> +	u32 flash_sf_address;	     // offset 0x038
> +	u32 flash_sf_data;		// offset 0x03c
> +	u32 flash_sf_timing;	      // offset 0x040
> +	u32 resv[3];
> +	u32 flash_pf_access;	      // offset 0x050

We don't need the offset commented on every member.  Typically it's not
done (which is annoying in debug / verification), but it is nice to see
after a reserved block.  Can you remove most of them so it's just
showing the post-resv offsets?  Thanks.

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 659 bytes
Desc: not available
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20201211/fa0e5d30/attachment.sig>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/2] board: presidio: Add Parallel NAND support
  2020-12-08 19:37 ` [PATCH 2/2] board: presidio: Add Parallel NAND support Alex Nemirovsky
@ 2020-12-11 20:54   ` Tom Rini
  2020-12-11 21:51     ` Alex Nemirovsky
  0 siblings, 1 reply; 5+ messages in thread
From: Tom Rini @ 2020-12-11 20:54 UTC (permalink / raw)
  To: u-boot

On Tue, Dec 08, 2020 at 11:37:37AM -0800, Alex Nemirovsky wrote:

> From: Kate Liu <kate.liu@cortina-access.com>
> 
> Set environment for Nand flash (U-boot 2020.04):
> - add nand flash in the device tree
> - add new default configuration file for G3 using parallel Nand
> - set nand parameters in presidio_asic.h
> 
> Signed-off-by: Kate Liu <kate.liu@cortina-access.com>
> Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
> CC: Tom Rini <trini@konsulko.com>
> ---
> 
>  MAINTAINERS                                   |  1 +
>  arch/arm/dts/ca-presidio-engboard.dts         | 14 +++++++++++
>  configs/cortina_presidio-asic-pnand_defconfig | 34 +++++++++++++++++++++++++++

A small thing, the defconfig being added here (and aside, I gather we
can't have a single defconfig for all variants?) needs to be
re-generated with savedefconfig.  There's a blank line in it which
implies that it wasn't.  Thanks.

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 659 bytes
Desc: not available
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20201211/022d3769/attachment.sig>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/2] board: presidio: Add Parallel NAND support
  2020-12-11 20:54   ` Tom Rini
@ 2020-12-11 21:51     ` Alex Nemirovsky
  0 siblings, 0 replies; 5+ messages in thread
From: Alex Nemirovsky @ 2020-12-11 21:51 UTC (permalink / raw)
  To: u-boot

Hi Tom,
after this NAND driver is in master, we can look at reducing the number
of defconfigs for this board and send out a new patch.

Thanks
-Alex

> On Dec 11, 2020, at 12:54 PM, Tom Rini <trini@konsulko.com> wrote:
> 
> On Tue, Dec 08, 2020 at 11:37:37AM -0800, Alex Nemirovsky wrote:
> 
>> From: Kate Liu <kate.liu@cortina-access.com>
>> 
>> Set environment for Nand flash (U-boot 2020.04):
>> - add nand flash in the device tree
>> - add new default configuration file for G3 using parallel Nand
>> - set nand parameters in presidio_asic.h
>> 
>> Signed-off-by: Kate Liu <kate.liu@cortina-access.com>
>> Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
>> CC: Tom Rini <trini@konsulko.com>
>> ---
>> 
>> MAINTAINERS                                   |  1 +
>> arch/arm/dts/ca-presidio-engboard.dts         | 14 +++++++++++
>> configs/cortina_presidio-asic-pnand_defconfig | 34 +++++++++++++++++++++++++++
> 
> A small thing, the defconfig being added here (and aside, I gather we
> can't have a single defconfig for all variants?) needs to be
> re-generated with savedefconfig.  There's a blank line in it which
> implies that it wasn't.  Thanks.
> 
> -- 
> Tom

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-12-11 21:51 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-08 19:37 [PATCH 1/2] mtd: rawnand: cortina_nand: Add Cortina CAxxxx SoC support Alex Nemirovsky
2020-12-08 19:37 ` [PATCH 2/2] board: presidio: Add Parallel NAND support Alex Nemirovsky
2020-12-11 20:54   ` Tom Rini
2020-12-11 21:51     ` Alex Nemirovsky
2020-12-11 20:54 ` [PATCH 1/2] mtd: rawnand: cortina_nand: Add Cortina CAxxxx SoC support Tom Rini

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.