mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH] mtd: nand: add pxa3xx-nand driver
@ 2015-01-01 21:58 Robert Jarzmik
  2015-01-04 18:50 ` Ezequiel Garcia
  0 siblings, 1 reply; 7+ messages in thread
From: Robert Jarzmik @ 2015-01-01 21:58 UTC (permalink / raw)
  To: barebox

The driver is taken from the Linux kernel, with the following changes :
 - all DMA removed
 - all asynchronous handling removed, including the interrupt handler,
   and the asynchronous state handling
 - pxa armada support removed

Most the kernel structure was kept, to ease up future fixes integration
from the kernel driver.

The driver is tested on a pxa3xx system development
board (aka. zylonite), and reading, writing, erasing, and bad block
management were tested.

Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
 drivers/mtd/nand/Kconfig                |    7 +
 drivers/mtd/nand/Makefile               |    1 +
 drivers/mtd/nand/nand_pxa3xx.c          | 1165 +++++++++++++++++++++++++++++++
 include/platform_data/mtd-nand-pxa3xx.h |   78 +++
 4 files changed, 1251 insertions(+)
 create mode 100644 drivers/mtd/nand/nand_pxa3xx.c
 create mode 100644 include/platform_data/mtd-nand-pxa3xx.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index c345847..b8e299b 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -97,6 +97,13 @@ config NAND_ORION
 	help
 	  Support for the Orion NAND controller, present in Kirkwood SoCs.
 
+config NAND_PXA3XX
+	bool
+	prompt "PXA3xx NAND driver"
+	depends on ARCH_PXA3XX
+	help
+	  Support for the PXA3xx NAND controller, present in pxa3xx SoCs.
+
 config NAND_ATMEL
 	bool
 	prompt "Atmel (AT91SAM9xxx) NAND driver"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 02dacde..79842dd 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_NAND_IMX)			+= nand_imx.o
 obj-$(CONFIG_NAND_IMX_BBM)		+= nand_imx_bbm.o
 obj-$(CONFIG_NAND_OMAP_GPMC)		+= nand_omap_gpmc.o nand_omap_bch_decoder.o
 obj-$(CONFIG_NAND_ORION)		+= nand_orion.o
+obj-$(CONFIG_NAND_PXA3XX)		+= nand_pxa3xx.o
 obj-$(CONFIG_NAND_ATMEL)		+= atmel_nand.o
 obj-$(CONFIG_NAND_S3C24XX)		+= nand_s3c24xx.o
 pbl-$(CONFIG_NAND_S3C24XX)		+= nand_s3c24xx.o
diff --git a/drivers/mtd/nand/nand_pxa3xx.c b/drivers/mtd/nand/nand_pxa3xx.c
new file mode 100644
index 0000000..83747be
--- /dev/null
+++ b/drivers/mtd/nand/nand_pxa3xx.c
@@ -0,0 +1,1165 @@
+/*
+ * drivers/mtd/nand/pxa3xx_nand.c
+ *
+ * Copyright © 2005 Intel Corporation
+ * Copyright © 2006 Marvell International Ltd.
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * See Documentation/mtd/nand/pxa3xx-nand.txt for more details.
+ */
+#include <common.h>
+
+#include <driver.h>
+#include <dma/apbh-dma.h>
+#include <errno.h>
+#include <clock.h>
+#include <init.h>
+#include <io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <mach/clock.h>
+#include <malloc.h>
+#include <of_mtd.h>
+#include <stmp-device.h>
+
+#include <platform_data/mtd-nand-pxa3xx.h>
+
+#define	CHIP_DELAY_TIMEOUT_US	500000
+#define PAGE_CHUNK_SIZE		(2048)
+
+/*
+ * Define a buffer size for the initial command that detects the flash device:
+ * STATUS, READID and PARAM. The largest of these is the PARAM command,
+ * needing 256 bytes.
+ */
+#define INIT_BUFFER_SIZE	256
+
+/* registers and bit definitions */
+#define NDCR		(0x00) /* Control register */
+#define NDTR0CS0	(0x04) /* Timing Parameter 0 for CS0 */
+#define NDTR1CS0	(0x0C) /* Timing Parameter 1 for CS0 */
+#define NDSR		(0x14) /* Status Register */
+#define NDPCR		(0x18) /* Page Count Register */
+#define NDBDR0		(0x1C) /* Bad Block Register 0 */
+#define NDBDR1		(0x20) /* Bad Block Register 1 */
+#define NDECCCTRL	(0x28) /* ECC control */
+#define NDDB		(0x40) /* Data Buffer */
+#define NDCB0		(0x48) /* Command Buffer0 */
+#define NDCB1		(0x4C) /* Command Buffer1 */
+#define NDCB2		(0x50) /* Command Buffer2 */
+
+#define NDCR_SPARE_EN		(0x1 << 31)
+#define NDCR_ECC_EN		(0x1 << 30)
+#define NDCR_DMA_EN		(0x1 << 29)
+#define NDCR_ND_RUN		(0x1 << 28)
+#define NDCR_DWIDTH_C		(0x1 << 27)
+#define NDCR_DWIDTH_M		(0x1 << 26)
+#define NDCR_PAGE_SZ		(0x1 << 24)
+#define NDCR_NCSX		(0x1 << 23)
+#define NDCR_ND_MODE		(0x3 << 21)
+#define NDCR_NAND_MODE		(0x0)
+#define NDCR_CLR_PG_CNT		(0x1 << 20)
+#define NDCR_STOP_ON_UNCOR	(0x1 << 19)
+#define NDCR_RD_ID_CNT_MASK	(0x7 << 16)
+#define NDCR_RD_ID_CNT(x)	(((x) << 16) & NDCR_RD_ID_CNT_MASK)
+
+#define NDCR_RA_START		(0x1 << 15)
+#define NDCR_PG_PER_BLK		(0x1 << 14)
+#define NDCR_ND_ARB_EN		(0x1 << 12)
+#define NDCR_INT_MASK           (0xFFF)
+
+#define NDSR_MASK		(0xfff)
+#define NDSR_ERR_CNT_OFF	(16)
+#define NDSR_ERR_CNT_MASK       (0x1f)
+#define NDSR_ERR_CNT(sr)	((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK)
+#define NDSR_RDY                (0x1 << 12)
+#define NDSR_FLASH_RDY          (0x1 << 11)
+#define NDSR_CS0_PAGED		(0x1 << 10)
+#define NDSR_CS1_PAGED		(0x1 << 9)
+#define NDSR_CS0_CMDD		(0x1 << 8)
+#define NDSR_CS1_CMDD		(0x1 << 7)
+#define NDSR_CS0_BBD		(0x1 << 6)
+#define NDSR_CS1_BBD		(0x1 << 5)
+#define NDSR_UNCORERR		(0x1 << 4)
+#define NDSR_CORERR		(0x1 << 3)
+#define NDSR_WRDREQ		(0x1 << 2)
+#define NDSR_RDDREQ		(0x1 << 1)
+#define NDSR_WRCMDREQ		(0x1)
+
+#define NDCB0_LEN_OVRD		(0x1 << 28)
+#define NDCB0_ST_ROW_EN         (0x1 << 26)
+#define NDCB0_AUTO_RS		(0x1 << 25)
+#define NDCB0_CSEL		(0x1 << 24)
+#define NDCB0_EXT_CMD_TYPE_MASK	(0x7 << 29)
+#define NDCB0_EXT_CMD_TYPE(x)	(((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK)
+#define NDCB0_CMD_TYPE_MASK	(0x7 << 21)
+#define NDCB0_CMD_TYPE(x)	(((x) << 21) & NDCB0_CMD_TYPE_MASK)
+#define NDCB0_NC		(0x1 << 20)
+#define NDCB0_DBC		(0x1 << 19)
+#define NDCB0_ADDR_CYC_MASK	(0x7 << 16)
+#define NDCB0_ADDR_CYC(x)	(((x) << 16) & NDCB0_ADDR_CYC_MASK)
+#define NDCB0_CMD2_MASK		(0xff << 8)
+#define NDCB0_CMD1_MASK		(0xff)
+#define NDCB0_ADDR_CYC_SHIFT	(16)
+
+#define EXT_CMD_TYPE_DISPATCH	6 /* Command dispatch */
+#define EXT_CMD_TYPE_NAKED_RW	5 /* Naked read or Naked write */
+#define EXT_CMD_TYPE_READ	4 /* Read */
+#define EXT_CMD_TYPE_DISP_WR	4 /* Command dispatch with write */
+#define EXT_CMD_TYPE_FINAL	3 /* Final command */
+#define EXT_CMD_TYPE_LAST_RW	1 /* Last naked read/write */
+#define EXT_CMD_TYPE_MONO	0 /* Monolithic read/write */
+
+/* macros for registers read/write */
+#define nand_writel(host, off, val) \
+	_nand_writel(__func__, __LINE__, (host), (off), (val))
+
+#define nand_writesl(host, off, buf, nbbytes)		\
+	writesl((host)->mmio_base + (off), buf, nbbytes)
+
+#define nand_readl(host, off)	\
+	_nand_readl(__func__, __LINE__, (host), (off))
+
+#define nand_readsl(host, off, buf, nbbytes)		\
+	readsl((host)->mmio_base + (off), buf, nbbytes)
+
+/* error code and state */
+enum {
+	ERR_NONE	= 0,
+	ERR_DMABUSERR	= -1,
+	ERR_SENDCMD	= -2,
+	ERR_UNCORERR	= -3,
+	ERR_BBERR	= -4,
+	ERR_CORERR	= -5,
+};
+
+struct pxa3xx_nand_host {
+	struct mtd_info		mtd;
+	struct nand_chip	chip;
+	struct mtd_partition	*parts;
+	struct device_d		*dev;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	void __iomem		*mmio_base;
+
+	unsigned int		buf_start;
+	unsigned int		buf_count;
+	unsigned int		buf_size;
+
+	unsigned char		*data_buff;
+
+	int			keep_config;
+	int			ecc_strength;
+	int			ecc_step;
+
+	int			cs;		/* selected chip 0/1 */
+	int			use_ecc;	/* use HW ECC ? */
+	int			use_spare;	/* use spare ? */
+	int			flash_bbt;
+
+	unsigned int		data_size;	/* data to be read from FIFO */
+	unsigned int		chunk_size;	/* split commands chunk size */
+	unsigned int		oob_size;
+	unsigned int		spare_size;
+	unsigned int		ecc_size;
+	unsigned int		max_bitflips;
+	int			cmd_ongoing;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0_chip0;
+	uint32_t		ndtr1cs0_chip0;
+	uint32_t		ndtr0cs0_chip1;
+	uint32_t		ndtr1cs0_chip1;
+
+	/* generated NDCBx register values */
+	uint32_t		ndcb0;
+	uint32_t		ndcb1;
+	uint32_t		ndcb2;
+	uint32_t		ndcb3;
+};
+
+static struct pxa3xx_nand_timing timing[] = {
+	{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
+	{ 10,  0, 20,  40, 30,  40, 11123, 110, 10, },
+	{ 10, 25, 15,  25, 15,  30, 25000,  60, 10, },
+	{ 10, 35, 15,  25, 15,  25, 25000,  60, 10, },
+};
+
+static struct pxa3xx_nand_flash builtin_flash_types[] = {
+{ "DEFAULT FLASH 8b",   0,   0, 2048,  8,  8,    0, &timing[0] },
+{ "DEFAULT FLASH 16b",  0,   0, 2048, 16, 16,    0, &timing[0] },
+{ "64MiB 16-bit",  0x46ec,  32,  512, 16, 16, 4096, &timing[1] },
+{ "256MiB 8-bit",  0xdaec,  64, 2048,  8,  8, 2048, &timing[1] },
+{ "4GiB 8-bit",    0xd7ec, 128, 4096,  8,  8, 8192, &timing[1] },
+{ "128MiB 8-bit",  0xa12c,  64, 2048,  8,  8, 1024, &timing[2] },
+{ "128MiB 16-bit", 0xb12c,  64, 2048, 16, 16, 1024, &timing[2] },
+{ "512MiB 8-bit",  0xdc2c,  64, 2048,  8,  8, 4096, &timing[2] },
+{ "512MiB 16-bit", 0xcc2c,  64, 2048, 16, 16, 4096, &timing[2] },
+{ "256MiB 16-bit", 0xba20,  64, 2048, 16, 16, 2048, &timing[3] },
+};
+
+static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
+static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.offs =	8,
+	.len = 6,
+	.veroffs = 14,
+	.maxblocks = 8,		/* Last 8 blocks in each chip */
+	.pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.offs =	8,
+	.len = 6,
+	.veroffs = 14,
+	.maxblocks = 8,		/* Last 8 blocks in each chip */
+	.pattern = bbt_mirror_pattern,
+};
+
+static struct nand_ecclayout ecc_layout_512B_hwecc = {
+	.eccbytes = 6,
+	.eccpos = {
+		8, 9, 10, 11, 12, 13, 14, 15 },
+	.oobfree = { {0, 8} }
+};
+
+static struct nand_ecclayout ecc_layout_2KB_hwecc = {
+	.eccbytes = 24,
+	.eccpos = {
+		40, 41, 42, 43, 44, 45, 46, 47,
+		48, 49, 50, 51, 52, 53, 54, 55,
+		56, 57, 58, 59, 60, 61, 62, 63 },
+	.oobfree = { {0, 40} }
+};
+
+/* Define a default flash type setting serve as flash detecting only */
+#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
+
+#define NDTR0_tCH(c)	(min((c), 7) << 19)
+#define NDTR0_tCS(c)	(min((c), 7) << 16)
+#define NDTR0_tWH(c)	(min((c), 7) << 11)
+#define NDTR0_tWP(c)	(min((c), 7) << 8)
+#define NDTR0_tRH(c)	(min((c), 7) << 3)
+#define NDTR0_tRP(c)	(min((c), 7) << 0)
+
+#define NDTR1_tR(c)	(min((c), 65535) << 16)
+#define NDTR1_tWHR(c)	(min((c), 15) << 4)
+#define NDTR1_tAR(c)	(min((c), 15) << 0)
+
+/* convert nano-seconds to nand flash controller clock cycles */
+#define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
+
+#define mtd_info_to_host(mtd) ((struct pxa3xx_nand_host *) \
+			       (((struct nand_chip *)((mtd)->priv))->priv))
+
+static struct of_device_id pxa3xx_nand_dt_ids[] = {
+	{
+		.compatible = "marvell,pxa3xx-nand",
+	},
+	{}
+};
+
+static volatile u32 _nand_readl(const char *func, const int line,
+		       struct pxa3xx_nand_host *host, int off)
+{
+	volatile u32 val = readl((host)->mmio_base + (off));
+
+	dev_vdbg(host->dev, "\treadl %s:%d reg=0x%08x => 0x%08x\n",
+		func, line, off, val);
+	return val;
+}
+
+static void _nand_writel(const char *func, const int line,
+			 struct pxa3xx_nand_host *host, int off, u32 val)
+{
+	dev_vdbg(host->dev, "\twritel %s:%d reg=0x%08x val=0x%08x\n",
+		 func, line, off, val);
+	writel(val, (host)->mmio_base + off);
+}
+
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, int chipnr,
+				   const struct pxa3xx_nand_timing *t)
+{
+	unsigned long nand_clk = pxa_get_nandclk();
+	uint32_t ndtr0, ndtr1;
+
+	/*
+	 * No timing in BootROM, keep current timings
+	 */
+	if (!t)
+		return;
+
+	ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
+		NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
+		NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
+		NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) |
+		NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) |
+		NDTR0_tRP(ns2cycle(t->tRP, nand_clk));
+
+	ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) |
+		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
+		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
+
+	if (chipnr == 1) {
+		host->ndtr0cs0_chip0 = ndtr0;
+		host->ndtr1cs0_chip0 = ndtr1;
+	} else {
+		host->ndtr0cs0_chip1 = ndtr0;
+		host->ndtr1cs0_chip1 = ndtr1;
+	}
+	nand_writel(host, NDTR0CS0, ndtr0);
+	nand_writel(host, NDTR1CS0, ndtr1);
+}
+
+static int pxa3xx_nand_ready(struct mtd_info *mtd)
+{
+	struct pxa3xx_nand_host *host = mtd_info_to_host(mtd);
+	u32 ndcr;
+
+	ndcr = nand_readl(host, NDSR);
+	if (host->cmd_ongoing == NAND_CMD_RESET)
+		if (host->cs == 0)
+			return ndcr & NDSR_FLASH_RDY;
+		if (host->cs == 1)
+			return ndcr & NDSR_RDY;
+	if (host->cs == 0)
+		return ndcr & NDSR_CS0_CMDD;
+	if (host->cs == 1)
+		return ndcr & NDSR_CS1_CMDD;
+	return 1;
+}
+
+/*
+ * Claims all blocks are good.
+ *
+ * In principle, this function is *only* called when the NAND Flash MTD system
+ * isn't allowed to keep an in-memory bad block table, so it is forced to ask
+ * the driver for bad block information.
+ *
+ * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so
+ * this function is *only* called when we take it away.
+ *
+ * Thus, this function is only called when we want *all* blocks to look good,
+ * so it *always* return success.
+ */
+static int pxa3xx_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	return 0;
+}
+
+static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+	struct pxa3xx_nand_host *host = mtd_info_to_host(mtd);
+	uint32_t ndtr0 = 0, ndtr1 = 0;
+
+	if (chipnr <= 0 || chipnr >= 3 || chipnr == host->cs)
+		return;
+	host->cs = chipnr - 1;
+	if (chipnr == 1) {
+		ndtr0 = host->ndtr0cs0_chip0;
+		ndtr1 = host->ndtr1cs0_chip0;
+	}
+	if (chipnr == 2) {
+		ndtr0 = host->ndtr0cs0_chip1;
+		ndtr1 = host->ndtr1cs0_chip1;
+	}
+	if (ndtr0 && ndtr1) {
+		nand_writel(host, NDTR0CS0, ndtr0);
+		nand_writel(host, NDTR1CS0, ndtr1);
+	}
+}
+
+/*
+ * Set the data and OOB size, depending on the selected
+ * spare and ECC configuration.
+ * Only applicable to READ0, READOOB and PAGEPROG commands.
+ */
+static unsigned int pxa3xx_datasize(struct pxa3xx_nand_host *host)
+{
+	unsigned int datasize;
+
+	datasize = host->mtd.writesize;
+	if (host->use_spare) {
+		datasize += host->spare_size;
+		if (!host->use_ecc)
+			datasize += host->ecc_size;
+	}
+	return datasize;
+}
+
+/**
+ * NOTE: it is a must to set ND_RUN firstly, then write
+ * command buffer, otherwise, it does not work.
+ * We enable all the interrupt at the same time, and
+ * let pxa3xx_nand_irq to handle all logic.
+ */
+static void pxa3xx_nand_start(struct pxa3xx_nand_host *host)
+{
+	uint32_t ndcr;
+
+	ndcr = host->reg_ndcr;
+	if (host->use_ecc)
+		ndcr |= NDCR_ECC_EN;
+	else
+		ndcr &= ~NDCR_ECC_EN;
+
+	ndcr &= ~NDCR_DMA_EN;
+
+	if (host->use_spare)
+		ndcr |= NDCR_SPARE_EN;
+	else
+		ndcr &= ~NDCR_SPARE_EN;
+
+	ndcr |= NDCR_ND_RUN;
+
+	/* clear status bits and run */
+	nand_writel(host, NDCR, 0);
+	nand_writel(host, NDSR, NDSR_MASK);
+	nand_writel(host, NDCR, ndcr);
+
+	/*
+	 * Writing 12 bytes to NDBC0 sets NDBC0, NDBC1 and NDBC2 !
+	 */
+	nand_writel(host, NDCB0, host->ndcb0);
+	nand_writel(host, NDCB0, host->ndcb1);
+	nand_writel(host, NDCB0, host->ndcb2);
+}
+
+static void disable_int(struct pxa3xx_nand_host *host, uint32_t int_mask)
+{
+	uint32_t ndcr;
+
+	ndcr = nand_readl(host, NDCR);
+	nand_writel(host, NDCR, ndcr | int_mask);
+}
+
+static inline int is_buf_blank(uint8_t *buf, size_t len)
+{
+	for (; len > 0; len--)
+		if (*buf++ != 0xff)
+			return 0;
+	return 1;
+}
+
+static void set_command_address(struct pxa3xx_nand_host *host,
+		unsigned int page_size, uint16_t column, int page_addr)
+{
+	/* small page addr setting */
+	if (page_size < PAGE_CHUNK_SIZE) {
+		host->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
+				| (column & 0xFF);
+
+		host->ndcb2 = 0;
+	} else {
+		host->ndcb1 = ((page_addr & 0xFFFF) << 16)
+				| (column & 0xFFFF);
+
+		if (page_addr & 0xFF0000)
+			host->ndcb2 = (page_addr & 0xFF0000) >> 16;
+		else
+			host->ndcb2 = 0;
+	}
+}
+
+static void prepare_start_command(struct pxa3xx_nand_host *host, int command)
+{
+	/* reset data and oob column point to handle data */
+	host->buf_start		= 0;
+	host->buf_count		= 0;
+	host->oob_size		= 0;
+	host->use_ecc		= 0;
+	host->use_spare		= 1;
+	host->ndcb3		= 0;
+	host->cmd_ongoing	= command;
+
+	switch (command) {
+	case NAND_CMD_SEQIN:
+		/*
+		 * This command is a no-op, as merged with PROGPAGE.
+		 */
+		break;
+	case NAND_CMD_READOOB:
+		host->data_size = pxa3xx_datasize(host);
+		break;
+	case NAND_CMD_READ0:
+		host->use_ecc = 1;
+		host->data_size = pxa3xx_datasize(host);
+		break;
+	case NAND_CMD_PAGEPROG:
+		host->use_ecc = 1;
+		host->data_size = pxa3xx_datasize(host);
+		break;
+	case NAND_CMD_PARAM:
+		host->use_spare = 0;
+		break;
+	default:
+		host->ndcb1 = 0;
+		host->ndcb2 = 0;
+		break;
+	}
+
+	/*
+	 * If we are about to issue a read command, or about to set
+	 * the write address, then clean the data buffer.
+	 */
+	if (command == NAND_CMD_READ0 ||
+	    command == NAND_CMD_READOOB ||
+	    command == NAND_CMD_SEQIN) {
+		host->buf_count = host->mtd.writesize + host->mtd.oobsize;
+		memset(host->data_buff, 0xFF, host->buf_count);
+	}
+
+}
+
+/**
+ * prepare_set_command - Prepare a NAND command
+ *
+ * Prepare data for a NAND command. If the command will not be executed, but
+ * instead merged into a "bi-command", returns 0.
+ *
+ * Returns if the command should be launched on the NFC
+ */
+static int prepare_set_command(struct pxa3xx_nand_host *host, int command,
+		int ext_cmd_type, uint16_t column, int page_addr)
+{
+	int addr_cycle, exec_cmd;
+	struct mtd_info *mtd;
+
+	mtd = &host->mtd;
+	exec_cmd = 1;
+
+	if (host->cs != 0)
+		host->ndcb0 = NDCB0_CSEL;
+	else
+		host->ndcb0 = 0;
+
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
+	switch (command) {
+	case NAND_CMD_READOOB:
+	case NAND_CMD_READ0:
+		host->ndcb0 |= NDCB0_CMD_TYPE(0)
+				| addr_cycle
+				| NAND_CMD_READ0;
+
+		if (command == NAND_CMD_READOOB)
+			host->buf_start = column + mtd->writesize;
+		else
+			host->buf_start = column;
+
+		/*
+		 * Multiple page read needs an 'extended command type' field,
+		 * which is either naked-read or last-read according to the
+		 * state.
+		 */
+		if (mtd->writesize == PAGE_CHUNK_SIZE) {
+			host->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
+		} else if (mtd->writesize > PAGE_CHUNK_SIZE) {
+			host->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
+					| NDCB0_LEN_OVRD
+					| NDCB0_EXT_CMD_TYPE(ext_cmd_type);
+			host->ndcb3 = host->chunk_size +
+				      host->oob_size;
+		}
+
+		set_command_address(host, mtd->writesize, column, page_addr);
+		break;
+
+	case NAND_CMD_SEQIN:
+		host->buf_start = column;
+		set_command_address(host, mtd->writesize, 0, page_addr);
+		/* Data transfer will occur in write_page */
+		host->data_size = 0;
+		exec_cmd = 0;
+		break;
+
+	case NAND_CMD_PAGEPROG:
+		host->ndcb0 |= NDCB0_CMD_TYPE(0x1)
+				| NDCB0_DBC
+				| (NAND_CMD_PAGEPROG << 8)
+				| NAND_CMD_SEQIN
+				| addr_cycle;
+		break;
+
+	case NAND_CMD_PARAM:
+		host->buf_count = 256;
+		host->ndcb0 |= NDCB0_CMD_TYPE(0)
+				| NDCB0_ADDR_CYC(1)
+				| NDCB0_LEN_OVRD
+				| command;
+		host->ndcb1 = (column & 0xFF);
+		host->ndcb3 = 256;
+		host->data_size = 256;
+		break;
+
+	case NAND_CMD_READID:
+		host->buf_count = host->read_id_bytes;
+		host->ndcb0 |= NDCB0_CMD_TYPE(3)
+				| NDCB0_ADDR_CYC(1)
+				| command;
+		host->ndcb1 = (column & 0xFF);
+
+		host->data_size = 8;
+		break;
+	case NAND_CMD_STATUS:
+		host->buf_count = 1;
+		host->ndcb0 |= NDCB0_CMD_TYPE(4)
+				| NDCB0_ADDR_CYC(1)
+				| command;
+
+		host->data_size = 8;
+		break;
+
+	case NAND_CMD_ERASE1:
+		host->ndcb0 |= NDCB0_CMD_TYPE(2)
+				| NDCB0_ADDR_CYC(3)
+				| NDCB0_DBC
+				| (NAND_CMD_ERASE2 << 8)
+				| NAND_CMD_ERASE1;
+		host->ndcb1 = page_addr;
+		host->ndcb2 = 0;
+
+		break;
+	case NAND_CMD_RESET:
+		host->ndcb0 |= NDCB0_CMD_TYPE(5)
+				| command;
+		break;
+
+	case NAND_CMD_ERASE2:
+		exec_cmd = 0;
+		break;
+
+	default:
+		exec_cmd = 0;
+		dev_err(host->dev, "non-supported command %x\n",
+				command);
+		break;
+	}
+
+	return exec_cmd;
+}
+
+static void pxa3xx_data_stage(struct pxa3xx_nand_host *host)
+{
+	unsigned int i, mask = NDSR_RDDREQ | NDSR_WRDREQ;
+	u32 *src, ndsr;
+
+	dev_dbg(host->dev, "%s() ndsr=0x%08x\n",  __func__,
+		nand_readl(host, NDSR));
+	if (!host->data_size)
+		return;
+
+	wait_on_timeout(host->chip.chip_delay * USECOND,
+			nand_readl(host, NDSR) & mask);
+	if (!(nand_readl(host, NDSR) & mask)) {
+		dev_err(host->dev, "Timeout waiting for data ndsr=0x%08x\n",
+			nand_readl(host, NDSR));
+		return;
+	}
+
+	ndsr = nand_readl(host, NDSR);
+	mask &= ndsr;
+	src = (u32 *)host->data_buff;
+
+	for (i = 0; i < host->data_size; i += 4) {
+		if (ndsr & NDSR_RDDREQ)
+			*src++ = nand_readl(host, NDDB);
+		if (ndsr & NDSR_WRDREQ)
+			nand_writel(host, NDDB, *src++);
+	}
+
+	host->data_size = 0;
+	nand_writel(host, NDSR, mask);
+}
+
+static void pxa3xx_nand_wait_cmd_done(struct pxa3xx_nand_host *host)
+{
+	unsigned int mask;
+
+	if (host->cs == 0)
+		mask = NDSR_CS0_CMDD | NDSR_WRCMDREQ;
+	else
+		mask = NDSR_CS1_CMDD | NDSR_WRCMDREQ;
+	wait_on_timeout(host->chip.chip_delay * USECOND,
+			(nand_readl(host, NDSR) & mask) == mask);
+	if ((nand_readl(host, NDSR) & mask) != mask)
+		dev_err(host->dev, "Waiting end of command timeout, ndsr=0x%08x\n",
+			nand_readl(host, NDSR) & mask);
+}
+
+static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+			       int column, int page_addr)
+{
+	struct pxa3xx_nand_host *host = mtd_info_to_host(mtd);
+
+	/*
+	 * if this is a x16 device ,then convert the input
+	 * "byte" address into a "word" address appropriate
+	 * for indexing a word-oriented device
+	 */
+	dev_dbg(host->dev, "%s(cmd=%d, col=%d, page=%d)\n", __func__,
+		command, column, page_addr);
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		column /= 2;
+
+	prepare_start_command(host, command);
+	if (prepare_set_command(host, command, 0, column, page_addr)) {
+		pxa3xx_nand_start(host);
+		pxa3xx_data_stage(host);
+		pxa3xx_nand_wait_cmd_done(host);
+	}
+}
+
+/**
+ * pxa3xx_nand_write_page_hwecc - prepare page for write
+ *
+ * Fills in the host->data_buff. The actual write will be done by the PAGEPROG
+ * command, which will trigger a pxa3xx_data_stage().
+ *
+ * Returns 0
+ */
+static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
+		struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+	struct pxa3xx_nand_host *host = mtd_info_to_host(mtd);
+
+	memcpy(host->data_buff, buf, mtd->writesize);
+	if (oob_required)
+		memcpy(host->data_buff + mtd->writesize, chip->oob_poi,
+		       mtd->oobsize);
+	else
+		memset(host->data_buff + mtd->writesize, 0, mtd->oobsize);
+	dev_dbg(host->dev, "%s(buf=%p, oob_required=%d) => 0\n",
+		__func__, buf, oob_required);
+	return 0;
+}
+
+static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
+		struct nand_chip *chip, uint8_t *buf, int oob_required,
+		int page)
+{
+	struct pxa3xx_nand_host *host = mtd_info_to_host(mtd);
+	u32 ndsr;
+	int ret = 0;
+
+	chip->read_buf(mtd, buf, mtd->writesize);
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	ndsr = nand_readl(host, NDSR);
+
+	if (ndsr & NDSR_UNCORERR) {
+		if (is_buf_blank(buf, mtd->writesize))
+			ret = 0;
+		else
+			ret = -EBADMSG;
+	}
+	if (ndsr & NDSR_CORERR)
+		ret = 1;
+	dev_dbg(host->dev, "%s(buf=%p, page=%d, oob_required=%d) => %d\n",
+		__func__, buf, page, oob_required, ret);
+	return ret;
+}
+
+static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct pxa3xx_nand_host *host = mtd_info_to_host(mtd);
+	int xfer;
+
+	xfer = min_t(int, len, host->buf_count);
+	memcpy(buf, host->data_buff + host->buf_start, xfer);
+	host->buf_start += xfer;
+	host->buf_count -= xfer;
+}
+
+static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
+{
+	uint8_t ret;
+
+	pxa3xx_nand_read_buf(mtd, (uint8_t *)&ret, sizeof(ret));
+	return ret;
+}
+
+static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
+{
+	u16 ret;
+
+	pxa3xx_nand_read_buf(mtd, (uint8_t *)&ret, sizeof(ret));
+	return ret;
+}
+
+static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
+		const uint8_t *buf, int len)
+{
+	struct pxa3xx_nand_host *host = mtd_info_to_host(mtd);
+
+	memcpy(host->data_buff + host->buf_start, buf, len);
+	host->buf_start += len;
+	host->buf_count -= len;
+}
+
+static int pxa3xx_nand_config_flash(struct pxa3xx_nand_host *host,
+				    const struct pxa3xx_nand_flash *f)
+{
+	uint32_t ndcr = 0x0;
+
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(host->dev, "Current only support 2048 and 512 size\n");
+		return -EINVAL;
+	}
+
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(host->dev, "Only support 8bit and 16 bit!\n");
+		return -EINVAL;
+	}
+
+	if (f->flash_width == 16)
+		host->chip.options |= NAND_BUSWIDTH_16;
+	else
+		host->chip.options &= ~NAND_BUSWIDTH_16;
+
+	/* calculate flash information */
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+
+	/* calculate addressing information */
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+
+	if (f->num_blocks * f->page_per_block > 65536)
+		host->row_addr_cycles = 3;
+	else
+		host->row_addr_cycles = 2;
+
+	ndcr |= NDCR_ND_ARB_EN;
+	ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0;
+	ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0;
+	ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0;
+	ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0;
+
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
+	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
+	ndcr &= ~NDCR_DMA_EN;
+
+	host->reg_ndcr = ndcr;
+
+	pxa3xx_nand_set_timing(host, host->cs, f->timing);
+	return 0;
+}
+
+static struct pxa3xx_nand_flash *pxa3xx_nand_detect_config(
+	struct pxa3xx_nand_host *host)
+{
+	static struct pxa3xx_nand_flash f = {
+		.name = "BootROM",
+		.chip_id = 0x0,
+	};
+
+	/*
+	 * We set 0 by hard coding here, for we don't support keep_config
+	 * when there is more than one chip attached to the controller
+	 */
+	uint32_t ndcr = nand_readl(host, NDCR);
+
+	f.page_per_block = (ndcr & NDCR_PG_PER_BLK) ? 64 : 32;
+	f.page_size = (ndcr & NDCR_PAGE_SZ) ? 2048 : 512;
+	f.flash_width = (ndcr & NDCR_DWIDTH_M) ? 16 : 8;
+	f.dfc_width = (ndcr & NDCR_DWIDTH_C) ? 16 : 8;
+
+	/* Set an initial chunk size */
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->ndtr0cs0_chip0 = nand_readl(host, NDTR0CS0);
+	host->ndtr1cs0_chip0 = nand_readl(host, NDTR1CS0);
+	host->ndtr0cs0_chip1 = nand_readl(host, NDTR0CS0);
+	host->ndtr1cs0_chip1 = nand_readl(host, NDTR1CS0);
+
+	return &f;
+}
+
+static int pxa3xx_nand_init_buff(struct pxa3xx_nand_host *host)
+{
+	host->data_buff = kmalloc(host->buf_size, GFP_KERNEL);
+	if (host->data_buff == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static struct pxa3xx_nand_flash *pxa3xx_nand_sensing(
+	struct pxa3xx_nand_host *host)
+{
+	struct nand_chip *chip = &host->chip;
+	struct pxa3xx_nand_flash *f;
+	int ret, i, id, flash_type;
+
+	for (flash_type = 0; flash_type < 2; flash_type++) {
+		/* use the common timing to make a try */
+		f = &builtin_flash_types[flash_type];
+		ret = pxa3xx_nand_config_flash(host, f);
+		chip->cmdfunc(&host->mtd, NAND_CMD_RESET, 0, 0);
+		nand_wait_ready(&host->mtd);
+		if (chip->dev_ready(&host->mtd))
+			break;
+	}
+	if (flash_type >= 2) {
+		dev_info(host->dev, "There is no chip on cs %d!\n",
+			 host->cs);
+		return ERR_PTR(-ENODEV);
+	}
+
+	chip->cmdfunc(&host->mtd, NAND_CMD_READID, 0, 0);
+	chip->read_buf(&host->mtd, host->data_buff, host->data_size);
+	nand_wait_ready(&host->mtd);
+
+	id = *(u16 *)(host->data_buff);
+	for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) {
+		f = &builtin_flash_types[i];
+		if (f->chip_id == id)
+			break;
+	}
+	if (i >= ARRAY_SIZE(builtin_flash_types)) {
+		dev_err(host->dev,
+			"Chip with flash id 0x%04x not in known types\n", id);
+		return ERR_PTR(-ENODEV);
+	}
+	return f;
+}
+
+static int pxa_ecc_init(struct pxa3xx_nand_host *host,
+			struct nand_ecc_ctrl *ecc,
+			int strength, int ecc_stepsize, int page_size)
+{
+	if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
+		host->chunk_size = 2048;
+		host->spare_size = 40;
+		host->ecc_size = 24;
+		ecc->mode = NAND_ECC_HW;
+		ecc->size = 512;
+		ecc->strength = 1;
+		ecc->layout = &ecc_layout_2KB_hwecc;
+
+	} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
+		host->chunk_size = 512;
+		host->spare_size = 8;
+		host->ecc_size = 8;
+		ecc->mode = NAND_ECC_HW;
+		ecc->size = 512;
+		ecc->layout = &ecc_layout_512B_hwecc;
+		ecc->strength = 1;
+	} else {
+		dev_err(host->dev,
+			"ECC strength %d at page size %d is not supported\n",
+			strength, page_size);
+		return -ENODEV;
+	}
+
+	dev_info(host->dev, "ECC strength %d, ECC step size %d\n",
+		 ecc->strength, ecc->size);
+	return 0;
+}
+
+static int pxa3xx_nand_scan(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct pxa3xx_nand_host *host = chip->priv;
+	const struct pxa3xx_nand_flash *f = NULL;
+	int ret;
+	uint16_t ecc_strength = host->ecc_strength;
+	uint16_t ecc_step = host->ecc_step;
+
+	if (host->keep_config)
+		f = pxa3xx_nand_detect_config(host);
+	else
+		f = pxa3xx_nand_sensing(host);
+	if (IS_ERR(f))
+		return PTR_ERR(f);
+
+	ret = pxa3xx_nand_config_flash(host, f);
+	if (ret) {
+		dev_err(host->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
+	dev_info(host->dev, "Found chip %s (id=0x%04x)\n",
+			 f->name, f->chip_id);
+	if (nand_scan_ident(mtd, 1, NULL))
+		return -ENODEV;
+
+	mtd->writesize = f->page_size;
+	mtd->erasesize = f->page_size * f->page_per_block;
+	mtd->size = (uint64_t)
+		f->num_blocks * f->page_per_block * f->page_size;
+	chip->chipsize = mtd->size;
+	chip->page_shift = ffs(mtd->writesize) - 1;
+	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
+
+	if (host->flash_bbt) {
+		/*
+		 * We'll use a bad block table stored in-flash and don't
+		 * allow writing the bad block marker to the flash.
+		 */
+		chip->bbt_options |= NAND_BBT_USE_FLASH |
+				     NAND_BBT_NO_OOB_BBM;
+		chip->bbt_td = &bbt_main_descr;
+		chip->bbt_md = &bbt_mirror_descr;
+	}
+
+	/*
+	 * If the page size is bigger than the FIFO size, let's check
+	 * we are given the right variant and then switch to the extended
+	 * (aka split) command handling,
+	 */
+	if (mtd->writesize > PAGE_CHUNK_SIZE) {
+		dev_err(host->dev,
+			"unsupported page size on this variant\n");
+		return -ENODEV;
+	}
+
+	/* Set default ECC strength requirements on non-ONFI devices */
+	if (ecc_strength < 1 && ecc_step < 1) {
+		ecc_strength = 1;
+		ecc_step = 512;
+	}
+
+	ret = pxa_ecc_init(host, &chip->ecc, ecc_strength,
+			   ecc_step, mtd->writesize);
+	if (ret)
+		return ret;
+	mtd->oobsize = host->spare_size + host->ecc_size;
+
+	/* calculate addressing information */
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
+	/* allocate the real data + oob buffer */
+	host->buf_size = mtd->writesize + mtd->oobsize;
+	ret = pxa3xx_nand_init_buff(host);
+	if (ret)
+		return ret;
+
+	if ((mtd->size >> chip->page_shift) > 65536)
+		host->row_addr_cycles = 3;
+	else
+		host->row_addr_cycles = 2;
+	return nand_scan_tail(mtd);
+}
+
+static struct pxa3xx_nand_host *alloc_nand_resource(struct device_d *dev)
+{
+	struct pxa3xx_nand_platform_data *pdata;
+	struct pxa3xx_nand_host *host;
+	struct nand_chip *chip = NULL;
+	struct mtd_info *mtd;
+
+	pdata = dev->platform_data;
+	host = xzalloc(sizeof(*host));
+	host->cs = 0;
+	mtd = &host->mtd;
+	mtd->priv = &host->chip;
+	mtd->parent = dev;
+	mtd->name = "pxa3xx_nand";
+
+	chip = &host->chip;
+	chip->read_byte		= pxa3xx_nand_read_byte;
+	chip->read_word		= pxa3xx_nand_read_word;
+	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+	chip->dev_ready		= pxa3xx_nand_ready;
+	chip->select_chip	= pxa3xx_nand_select_chip;
+	chip->block_bad		= pxa3xx_nand_block_bad;
+	chip->read_buf		= pxa3xx_nand_read_buf;
+	chip->write_buf		= pxa3xx_nand_write_buf;
+	chip->options		|= NAND_NO_SUBPAGE_WRITE;
+	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+	chip->priv		= host;
+	chip->chip_delay	= CHIP_DELAY_TIMEOUT_US;
+
+	host->dev = dev;
+	host->mmio_base = dev_request_mem_region(dev, 0);
+	if (IS_ERR(host->mmio_base)) {
+		free(host);
+		return host->mmio_base;
+	}
+	if (pdata) {
+		host->keep_config = pdata->keep_config;
+		host->flash_bbt = pdata->flash_bbt;
+		host->ecc_strength = pdata->ecc_strength;
+		host->ecc_step = pdata->ecc_step_size;
+	}
+
+	/* Allocate a buffer to allow flash detection */
+	host->buf_size = INIT_BUFFER_SIZE;
+	host->data_buff = xmalloc(host->buf_size);
+
+	/* initialize all interrupts to be disabled */
+	disable_int(host, NDSR_MASK);
+	return host;
+}
+
+static int pxa3xx_nand_probe_dt(struct pxa3xx_nand_host *host)
+{
+	struct device_node *np = host->dev->device_node;
+
+	if (of_get_property(np, "marvell,nand-keep-config", NULL))
+		host->keep_config = 1;
+	of_property_read_u32(np, "num-cs", &host->cs);
+	if (of_get_nand_on_flash_bbt(np))
+		host->flash_bbt = 1;
+
+	return 0;
+}
+
+static int pxa3xx_nand_probe(struct device_d *dev)
+{
+	struct pxa3xx_nand_host *host;
+	int ret;
+
+	host = alloc_nand_resource(dev);
+	if (IS_ERR(host)) {
+		dev_err(dev, "alloc nand resource failed\n");
+		return PTR_ERR(host);
+	}
+
+	ret = pxa3xx_nand_probe_dt(host);
+	if (ret)
+		return ret;
+
+	host->chip.controller = &host->chip.hwcontrol;
+	ret = pxa3xx_nand_scan(&host->mtd);
+	if (ret) {
+		dev_warn(dev, "failed to scan nand at cs %d\n",
+			 host->cs);
+		return -ENODEV;
+	}
+
+	ret = add_mtd_nand_device(&host->mtd, "nand");
+	return ret;
+}
+
+static struct driver_d pxa3xx_nand_driver = {
+	.name		= "pxa3xx_nand",
+	.probe		= pxa3xx_nand_probe,
+	.of_compatible	= DRV_OF_COMPAT(pxa3xx_nand_dt_ids),
+};
+device_platform_driver(pxa3xx_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PXA3xx NAND controller driver");
diff --git a/include/platform_data/mtd-nand-pxa3xx.h b/include/platform_data/mtd-nand-pxa3xx.h
new file mode 100644
index 0000000..33b0ff5
--- /dev/null
+++ b/include/platform_data/mtd-nand-pxa3xx.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Taken from linux kernel mostly.
+ */
+#ifndef __ASM_ARCH_PXA3XX_NAND_H
+#define __ASM_ARCH_PXA3XX_NAND_H
+
+struct pxa3xx_nand_timing {
+	unsigned int	tCH;  /* Enable signal hold time */
+	unsigned int	tCS;  /* Enable signal setup time */
+	unsigned int	tWH;  /* ND_nWE high duration */
+	unsigned int	tWP;  /* ND_nWE pulse time */
+	unsigned int	tRH;  /* ND_nRE high duration */
+	unsigned int	tRP;  /* ND_nRE pulse width */
+	unsigned int	tR;   /* ND_nWE high to ND_nRE low for read */
+	unsigned int	tWHR; /* ND_nWE high to ND_nRE low for status read */
+	unsigned int	tAR;  /* ND_ALE low to ND_nRE low delay */
+};
+
+struct pxa3xx_nand_flash {
+	char		*name;
+	uint32_t	chip_id;
+	unsigned int	page_per_block; /* Pages per block (PG_PER_BLK) */
+	unsigned int	page_size;	/* Page size in bytes (PAGE_SZ) */
+	unsigned int	flash_width;	/* Flash memory width (DWIDTH_M) */
+	unsigned int	dfc_width;	/* Flash controller width (DWIDTH_C) */
+	unsigned int	num_blocks;	/* Number of physical blocks in Flash */
+
+	struct pxa3xx_nand_timing *timing;	/* NAND Flash timing */
+};
+
+/*
+ * Current pxa3xx_nand controller has two chip select which
+ * both be workable.
+ *
+ * Notice should be taken that:
+ * When you want to use this feature, you should not enable the
+ * keep configuration feature, for two chip select could be
+ * attached with different nand chip. The different page size
+ * and timing requirement make the keep configuration impossible.
+ */
+
+/* The max num of chip select current support */
+#define NUM_CHIP_SELECT		(2)
+struct pxa3xx_nand_platform_data {
+	/* the data flash bus is shared between the Static Memory
+	 * Controller and the Data Flash Controller,  the arbiter
+	 * controls the ownership of the bus
+	 */
+	int	dwidth_c;
+	int	dwidth_m;
+
+	/* allow platform code to keep OBM/bootloader defined NFC config */
+	int	keep_config;
+
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	/* use an flash-based bad block table */
+	bool	flash_bbt;
+
+	/* requested ECC strength and ECC step size */
+	int ecc_strength, ecc_step_size;
+
+	const struct mtd_partition		*parts[NUM_CHIP_SELECT];
+	unsigned int				nr_parts[NUM_CHIP_SELECT];
+
+	const struct pxa3xx_nand_flash		*flash;
+	size_t					num_flash;
+};
+
+extern void pxa3xx_set_nand_info(struct pxa3xx_nand_platform_data *info);
+#endif /* __ASM_ARCH_PXA3XX_NAND_H */
-- 
2.1.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH] mtd: nand: add pxa3xx-nand driver
  2015-01-01 21:58 [PATCH] mtd: nand: add pxa3xx-nand driver Robert Jarzmik
@ 2015-01-04 18:50 ` Ezequiel Garcia
  2015-01-05 10:44   ` robert.jarzmik
  0 siblings, 1 reply; 7+ messages in thread
From: Ezequiel Garcia @ 2015-01-04 18:50 UTC (permalink / raw)
  To: Robert Jarzmik, barebox

Hi Robert,

On 01/01/2015 06:58 PM, Robert Jarzmik wrote:
> The driver is taken from the Linux kernel, with the following changes :
>  - all DMA removed
>  - all asynchronous handling removed, including the interrupt handler,
>    and the asynchronous state handling
>  - pxa armada support removed
> 

If my memory doesn't fail me, the NAND controller is called NFC v1 on
pxa3xx, and NFC v2 on mvebu SoCs.

I'm wondering if we can pick a better name for this driver in barebox.
Maybe something like 'mv_nfc_nand' ?

Also, I think you can get rid of the 'builtin_flash_types' struct and
all the device detection routines, which are really duplicating the
MTD's lower level job.

I never removed it from the Linux driver, because I was afraid of
regressions and there are little testers around. However, on Barebox we
can benefit from removing that, and starting with a cleaner driver.

Just my two cents,
-- 
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH] mtd: nand: add pxa3xx-nand driver
  2015-01-04 18:50 ` Ezequiel Garcia
@ 2015-01-05 10:44   ` robert.jarzmik
  2015-01-05 12:03     ` Sascha Hauer
  0 siblings, 1 reply; 7+ messages in thread
From: robert.jarzmik @ 2015-01-05 10:44 UTC (permalink / raw)
  To: Ezequiel Garcia; +Cc: barebox

----- Mail original -----
De: "Ezequiel Garcia" <ezequiel.garcia@free-electrons.com>
À: "Robert Jarzmik" <robert.jarzmik@free.fr>, barebox@lists.infradead.org
Envoyé: Dimanche 4 Janvier 2015 19:50:55
Objet: Re: [PATCH] mtd: nand: add pxa3xx-nand driver

Hi Robert,

> If my memory doesn't fail me, the NAND controller is called NFC v1 on
> pxa3xx, and NFC v2 on mvebu SoCs.
> I'm wondering if we can pick a better name for this driver in barebox.
> Maybe something like 'mv_nfc_nand' ?
As all drivers are called "nand_XXX", I'd rather have it nand_mrvl_nfc then.
Does that sound good to you ?

> Also, I think you can get rid of the 'builtin_flash_types' struct and
> all the device detection routines, which are really duplicating the
> MTD's lower level job.
Well, not really. If I get rid of them, how will I get the following
information :
 - page size
 - nand timings (tHP, ...)
 - controller width to use
 - ...

> I never removed it from the Linux driver, because I was afraid of
> regressions and there are little testers around. However, on Barebox we
> can benefit from removing that, and starting with a cleaner driver.
If you convince me all the information can be found back, I'll gladly
remove them.

Cheers.

--
Robert

-- 
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH] mtd: nand: add pxa3xx-nand driver
  2015-01-05 10:44   ` robert.jarzmik
@ 2015-01-05 12:03     ` Sascha Hauer
  2015-01-05 20:32       ` Robert Jarzmik
  0 siblings, 1 reply; 7+ messages in thread
From: Sascha Hauer @ 2015-01-05 12:03 UTC (permalink / raw)
  To: robert.jarzmik; +Cc: barebox

Hi Robert,

On Mon, Jan 05, 2015 at 11:44:38AM +0100, robert.jarzmik@free.fr wrote:
> ----- Mail original -----
> De: "Ezequiel Garcia" <ezequiel.garcia@free-electrons.com>
> À: "Robert Jarzmik" <robert.jarzmik@free.fr>, barebox@lists.infradead.org
> Envoyé: Dimanche 4 Janvier 2015 19:50:55
> Objet: Re: [PATCH] mtd: nand: add pxa3xx-nand driver
> 
> Hi Robert,
> 
> > If my memory doesn't fail me, the NAND controller is called NFC v1 on
> > pxa3xx, and NFC v2 on mvebu SoCs.
> > I'm wondering if we can pick a better name for this driver in barebox.
> > Maybe something like 'mv_nfc_nand' ?
> As all drivers are called "nand_XXX", I'd rather have it nand_mrvl_nfc then.
> Does that sound good to you ?
> 
> > Also, I think you can get rid of the 'builtin_flash_types' struct and
> > all the device detection routines, which are really duplicating the
> > MTD's lower level job.
> Well, not really. If I get rid of them, how will I get the following
> information :
>  - page size
>  - nand timings (tHP, ...)
>  - controller width to use
>  - ...

Things like mtd->writesize, mtd->erasesize, and mtd->size should be
initialized after calling nand_scan_ident(). These are currently
overwritten by the pxa nand driver, but normally you should be able to
remove this. IIRC barebox can also detect the chip widths automatically.
I don't know how to get the timings though.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH] mtd: nand: add pxa3xx-nand driver
  2015-01-05 12:03     ` Sascha Hauer
@ 2015-01-05 20:32       ` Robert Jarzmik
  2015-01-05 20:33         ` Ezequiel Garcia
  0 siblings, 1 reply; 7+ messages in thread
From: Robert Jarzmik @ 2015-01-05 20:32 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

Sascha Hauer <s.hauer@pengutronix.de> writes:

> Hi Robert,
>
> Things like mtd->writesize, mtd->erasesize, and mtd->size should be
> initialized after calling nand_scan_ident(). These are currently
> overwritten by the pxa nand driver, but normally you should be able to
> remove this.
You're right, the extended id parsing should take care of that.

> IIRC barebox can also detect the chip widths automatically.
Yup, that's also in the extended id parsing.

> I don't know how to get the timings though.
I have an idea here : as barebox is not performance oriented, and as the timings
are minimum guaranteed times between signal swaps, let me try to hard encode all
timing with their maximum value.

Of course performance will suffer, but as long it remains usable with a loss of
20% or less in throughput, that will be a cool simplification.

Cheers.

-- 
Robert

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH] mtd: nand: add pxa3xx-nand driver
  2015-01-05 20:32       ` Robert Jarzmik
@ 2015-01-05 20:33         ` Ezequiel Garcia
  2015-01-05 22:42           ` Robert Jarzmik
  0 siblings, 1 reply; 7+ messages in thread
From: Ezequiel Garcia @ 2015-01-05 20:33 UTC (permalink / raw)
  To: Robert Jarzmik, Sascha Hauer; +Cc: barebox

On 01/05/2015 05:32 PM, Robert Jarzmik wrote:
> I have an idea here : as barebox is not performance oriented, and as the timings
> are minimum guaranteed times between signal swaps, let me try to hard encode all
> timing with their maximum value.

Sounds a good plan to me.
-- 
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH] mtd: nand: add pxa3xx-nand driver
  2015-01-05 20:33         ` Ezequiel Garcia
@ 2015-01-05 22:42           ` Robert Jarzmik
  0 siblings, 0 replies; 7+ messages in thread
From: Robert Jarzmik @ 2015-01-05 22:42 UTC (permalink / raw)
  To: Ezequiel Garcia; +Cc: barebox

Ezequiel Garcia <ezequiel.garcia@free-electrons.com> writes:

> On 01/05/2015 05:32 PM, Robert Jarzmik wrote:
>> I have an idea here : as barebox is not performance oriented, and as the timings
>> are minimum guaranteed times between signal swaps, let me try to hard encode all
>> timing with their maximum value.
>
> Sounds a good plan to me.

And unfortunately it doesn't work. Too relaxed timings imply "fake" bytes
returned by the flash NAND to the NAND controler.

So I have to keep a table of timings at least.

Cheers.

-- 
Robert

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

end of thread, other threads:[~2015-01-05 22:42 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-01 21:58 [PATCH] mtd: nand: add pxa3xx-nand driver Robert Jarzmik
2015-01-04 18:50 ` Ezequiel Garcia
2015-01-05 10:44   ` robert.jarzmik
2015-01-05 12:03     ` Sascha Hauer
2015-01-05 20:32       ` Robert Jarzmik
2015-01-05 20:33         ` Ezequiel Garcia
2015-01-05 22:42           ` Robert Jarzmik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox