All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-08 16:00 ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-22  3:17 UTC (permalink / raw)
  To: Haojian Zhuang, Eric Miao, David Woodhouse, Artem Bityutskiy,
	linux-mtd, linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
 9 files changed, 444 insertions(+), 338 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..b6589d6 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.cs_num = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..d67eb7b 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..ff7a07b 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..6eaf852 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..e7ce135 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.cs_num		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..a54846d 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..ea4752a 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..34a3f52 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,8 @@ struct pxa3xx_nand_flash {
 	struct pxa3xx_nand_timing *timing;	/* NAND Flash timing */
 };
 
+/* 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
@@ -52,8 +54,10 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip select would be used for this platform */
+	int	cs_num;
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 30689cc..259b8d5 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -92,11 +92,13 @@
 #define NDCB0_ADDR_CYC_SHIFT	(16)
 
 /* macros for registers read/write */
-#define nand_writel(info, off, val)	\
-	__raw_writel((val), (info)->mmio_base + (off))
+#define nand_writel(nand, off, val)	\
+	__raw_writel((val), (nand)->mmio_base + (off))
 
-#define nand_readl(info, off)		\
-	__raw_readl((info)->mmio_base + (off))
+#define nand_readl(nand, off)		\
+	__raw_readl((nand)->mmio_base + (off))
+#define get_mtd_by_info(info)		\
+	(struct mtd_info *)((void *)info - sizeof(struct mtd_info))
 
 /* error code and state */
 enum {
@@ -110,6 +112,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -123,63 +126,63 @@ enum {
 struct pxa3xx_nand_info {
 	struct nand_chip	nand_chip;
 
-	struct nand_hw_control	controller;
-	struct platform_device	 *pdev;
 	struct pxa3xx_nand_cmdset *cmdset;
+	/* page size of attached chip */
+	uint16_t		page_size;
+	uint8_t			chip_select;
+	uint8_t			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	uint8_t			col_addr_cycles;
+	uint8_t			row_addr_cycles;
+	uint8_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
 
+	void			*nand_data;
+};
+
+struct pxa3xx_nand {
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct nand_hw_control	controller;
+	struct completion	cmd_complete;
+	struct platform_device	 *pdev;
 
-	unsigned int 		buf_start;
-	unsigned int		buf_count;
-
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
-
-	unsigned char		*data_buff;
-	unsigned char		*oob_buff;
-	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
-	struct pxa_dma_desc	*data_desc;
+	dma_addr_t		data_buff_phys;
 	dma_addr_t 		data_desc_addr;
+	struct pxa_dma_desc	*data_desc;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
+	struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
+	uint32_t		command;
+	uint16_t		data_size;	/* data size in FIFO */
+	uint16_t		oob_size;
+	unsigned char		*data_buff;
+	unsigned char		*oob_buff;
+	uint32_t		buf_start;
+	uint32_t		buf_count;
 
 	/* relate to the command */
 	unsigned int		state;
-
+	uint8_t			chip_select;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
-
-	unsigned int		page_size;	/* page size of attached chip */
-	unsigned int		data_size;	/* data size in FIFO */
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
+	uint8_t			total_cmds;
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 				   const struct pxa3xx_nand_timing *t)
 {
-	unsigned long nand_clk = clk_get_rate(info->clk);
+	struct pxa3xx_nand *nand = info->nand_data;
+	unsigned long nand_clk;
 	uint32_t ndtr0, ndtr1;
 
+	nand_clk = clk_get_rate(nand->clk);
 	ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
 		NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
 		NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
@@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 
 	info->ndtr0cs0 = ndtr0;
 	info->ndtr1cs0 = ndtr1;
-	nand_writel(info, NDTR0CS0, ndtr0);
-	nand_writel(info, NDTR1CS0, ndtr1);
+	nand_writel(nand, NDTR0CS0, ndtr0);
+	nand_writel(nand, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand *nand = info->nand_data;
 	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	nand->data_size = info->page_size;
 	if (!oob_enable) {
-		info->oob_size = 0;
+		nand->oob_size = 0;
 		return;
 	}
 
 	switch (info->page_size) {
 	case 2048:
-		info->oob_size = (info->use_ecc) ? 40 : 64;
+		nand->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
 	case 512:
-		info->oob_size = (info->use_ecc) ? 8 : 16;
+		nand->oob_size = (info->use_ecc) ? 8 : 16;
 		break;
 	}
 }
@@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  * 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_info *info)
+static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
 {
+	struct pxa3xx_nand_info *info;
 	uint32_t ndcr;
 
+	info = nand->info[nand->chip_select];
 	ndcr = info->reg_ndcr;
-	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
-	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
+	ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
+	ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
 
 	/* clear status bits and run */
-	nand_writel(info, NDCR, 0);
-	nand_writel(info, NDSR, NDSR_MASK);
-	nand_writel(info, NDCR, ndcr);
+	nand_writel(nand, NDCR, 0);
+	nand_writel(nand, NDSR, NDSR_MASK);
+	nand_writel(nand, NDCR, ndcr);
 }
 
-static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
+static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
 {
 	uint32_t ndcr;
 	int timeout = NAND_STOP_DELAY;
 
 	/* wait RUN bit in NDCR become 0 */
-	ndcr = nand_readl(info, NDCR);
+	ndcr = nand_readl(nand, NDCR);
 	while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
-		ndcr = nand_readl(info, NDCR);
+		ndcr = nand_readl(nand, NDCR);
 		udelay(1);
 	}
 
 	if (timeout <= 0) {
 		ndcr &= ~NDCR_ND_RUN;
-		nand_writel(info, NDCR, ndcr);
+		nand_writel(nand, NDCR, ndcr);
 	}
 	/* clear status bits */
-	nand_writel(info, NDSR, NDSR_MASK);
+	nand_writel(nand, NDSR, NDSR_MASK);
 }
 
-static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
+static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
 {
 	uint32_t ndcr;
 
-	ndcr = nand_readl(info, NDCR);
-	nand_writel(info, NDCR, ndcr & ~int_mask);
+	ndcr = nand_readl(nand, NDCR);
+	nand_writel(nand, NDCR, ndcr & ~int_mask);
 }
 
-static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
+static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
 {
 	uint32_t ndcr;
 
-	ndcr = nand_readl(info, NDCR);
-	nand_writel(info, NDCR, ndcr | int_mask);
+	ndcr = nand_readl(nand, NDCR);
+	nand_writel(nand, NDCR, ndcr | int_mask);
 }
 
-static void handle_data_pio(struct pxa3xx_nand_info *info)
+static void handle_data_pio(struct pxa3xx_nand *nand)
 {
-	switch (info->state) {
+	switch (nand->state) {
 	case STATE_PIO_WRITING:
-		__raw_writesl(info->mmio_base + NDDB, info->data_buff,
-				DIV_ROUND_UP(info->data_size, 4));
-		if (info->oob_size > 0)
-			__raw_writesl(info->mmio_base + NDDB, info->oob_buff,
-					DIV_ROUND_UP(info->oob_size, 4));
+		__raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
+				DIV_ROUND_UP(nand->data_size, 4));
+		if (nand->oob_size > 0)
+			__raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
+					DIV_ROUND_UP(nand->oob_size, 4));
 		break;
 	case STATE_PIO_READING:
-		__raw_readsl(info->mmio_base + NDDB, info->data_buff,
-				DIV_ROUND_UP(info->data_size, 4));
-		if (info->oob_size > 0)
-			__raw_readsl(info->mmio_base + NDDB, info->oob_buff,
-					DIV_ROUND_UP(info->oob_size, 4));
+		__raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
+				DIV_ROUND_UP(nand->data_size, 4));
+		if (nand->oob_size > 0)
+			__raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
+					DIV_ROUND_UP(nand->oob_size, 4));
 		break;
 	default:
 		printk(KERN_ERR "%s: invalid state %d\n", __func__,
-				info->state);
+				nand->state);
 		BUG();
 	}
 }
 
-static void start_data_dma(struct pxa3xx_nand_info *info)
+static void start_data_dma(struct pxa3xx_nand *nand)
 {
-	struct pxa_dma_desc *desc = info->data_desc;
-	int dma_len = ALIGN(info->data_size + info->oob_size, 32);
+	struct pxa_dma_desc *desc = nand->data_desc;
+	int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
 
 	desc->ddadr = DDADR_STOP;
 	desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
 
-	switch (info->state) {
+	switch (nand->state) {
 	case STATE_DMA_WRITING:
-		desc->dsadr = info->data_buff_phys;
-		desc->dtadr = info->mmio_phys + NDDB;
+		desc->dsadr = nand->data_buff_phys;
+		desc->dtadr = nand->mmio_phys + NDDB;
 		desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
 		break;
 	case STATE_DMA_READING:
-		desc->dtadr = info->data_buff_phys;
-		desc->dsadr = info->mmio_phys + NDDB;
+		desc->dtadr = nand->data_buff_phys;
+		desc->dsadr = nand->mmio_phys + NDDB;
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
 		printk(KERN_ERR "%s: invalid state %d\n", __func__,
-				info->state);
+				nand->state);
 		BUG();
 	}
 
-	DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
-	DDADR(info->data_dma_ch) = info->data_desc_addr;
-	DCSR(info->data_dma_ch) |= DCSR_RUN;
+	DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
+	DDADR(nand->data_dma_ch) = nand->data_desc_addr;
+	DCSR(nand->data_dma_ch) |= DCSR_RUN;
 }
 
 static void pxa3xx_nand_data_dma_irq(int channel, void *data)
 {
-	struct pxa3xx_nand_info *info = data;
+	struct pxa3xx_nand *nand = data;
 	uint32_t dcsr;
 
 	dcsr = DCSR(channel);
 	DCSR(channel) = dcsr;
 
 	if (dcsr & DCSR_BUSERR) {
-		info->retcode = ERR_DMABUSERR;
+		nand->retcode = ERR_DMABUSERR;
 	}
 
-	info->state = STATE_DMA_DONE;
-	enable_int(info, NDCR_INT_MASK);
-	nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
+	nand->state = STATE_DMA_DONE;
+	enable_int(nand, NDCR_INT_MASK);
+	nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
 }
 
 static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
-	struct pxa3xx_nand_info *info = devid;
-	unsigned int status, is_completed = 0;
+	struct pxa3xx_nand *nand = devid;
+	struct pxa3xx_nand_info *info;
+	unsigned int status, is_completed = 0, cs;
+	unsigned int ready, cmd_done, page_done, badblock_detect;
 
-	status = nand_readl(info, NDSR);
+	cs		= nand->chip_select;
+	ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
+	cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
+	page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
+	badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
+	info            = nand->info[cs];
 
+	status = nand_readl(nand, NDSR);
 	if (status & NDSR_DBERR)
-		info->retcode = ERR_DBERR;
+		nand->retcode = ERR_DBERR;
 	if (status & NDSR_SBERR)
-		info->retcode = ERR_SBERR;
+		nand->retcode = ERR_SBERR;
 	if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
 		/* whether use dma to transfer data */
-		if (info->use_dma) {
-			disable_int(info, NDCR_INT_MASK);
-			info->state = (status & NDSR_RDDREQ) ?
+		if (nand->use_dma) {
+			disable_int(nand, NDCR_INT_MASK);
+			nand->state = (status & NDSR_RDDREQ) ?
 				      STATE_DMA_READING : STATE_DMA_WRITING;
-			start_data_dma(info);
+			start_data_dma(nand);
 			goto NORMAL_IRQ_EXIT;
 		} else {
-			info->state = (status & NDSR_RDDREQ) ?
+			nand->state = (status & NDSR_RDDREQ) ?
 				      STATE_PIO_READING : STATE_PIO_WRITING;
-			handle_data_pio(info);
+			handle_data_pio(nand);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
-		info->state = STATE_CMD_DONE;
+	if (status & cmd_done) {
+		nand->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
-		info->is_ready = 1;
-		info->state = STATE_READY;
+	if (status & ready) {
+		nand->is_ready = 1;
+		nand->state = STATE_READY;
 	}
 
 	if (status & NDSR_WRCMDREQ) {
-		nand_writel(info, NDSR, NDSR_WRCMDREQ);
+		nand_writel(nand, NDSR, NDSR_WRCMDREQ);
 		status &= ~NDSR_WRCMDREQ;
-		info->state = STATE_CMD_HANDLE;
-		nand_writel(info, NDCB0, info->ndcb0);
-		nand_writel(info, NDCB0, info->ndcb1);
-		nand_writel(info, NDCB0, info->ndcb2);
+		nand->state = STATE_CMD_HANDLE;
+		nand_writel(nand, NDCB0, nand->ndcb0);
+		nand_writel(nand, NDCB0, nand->ndcb1);
+		nand_writel(nand, NDCB0, nand->ndcb2);
 	}
 
 	/* clear NDSR to let the controller exit the IRQ */
-	nand_writel(info, NDSR, status);
+	nand_writel(nand, NDSR, status);
 	if (is_completed)
-		complete(&info->cmd_complete);
+		complete(&nand->cmd_complete);
 NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
 	return 1;
 }
 
-static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
+static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	struct mtd_info *mtd;
+	struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
 
-	ndcb0 = 0;
+	mtd = get_mtd_by_info(info);
+	ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
 	/* reset data and oob column point to handle data */
-	info->buf_start		= 0;
-	info->buf_count		= 0;
-	info->oob_size		= 0;
-	info->use_ecc		= 0;
-	info->is_ready		= 0;
-	info->retcode		= ERR_NONE;
+	nand->buf_start		= 0;
+	nand->buf_count		= 0;
+	nand->oob_size		= 0;
+	nand->use_ecc		= 0;
+	nand->is_ready		= 0;
+	nand->retcode		= ERR_NONE;
+	nand->data_size		= 0;
+	nand->use_dma		= 0;
+	nand->command		= command;
 
 	switch (command) {
 	case NAND_CMD_READ0:
 	case NAND_CMD_PAGEPROG:
-		info->use_ecc = 1;
+		nand->use_ecc = 1;
 	case NAND_CMD_READOOB:
 		pxa3xx_set_datasize(info);
+		nand->oob_buff = nand->data_buff + nand->data_size;
+		nand->use_dma = use_dma;
 		break;
 	case NAND_CMD_SEQIN:
 		exec_cmd = 0;
 		break;
 	default:
-		info->ndcb1 = 0;
-		info->ndcb2 = 0;
+		nand->ndcb1 = 0;
+		nand->ndcb2 = 0;
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
+	nand->ndcb0 = ndcb0;
 	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
 				    + info->col_addr_cycles);
 
@@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	case NAND_CMD_READ0:
 		cmd = info->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
-			info->buf_start = mtd->writesize + column;
+			nand->buf_start = mtd->writesize + column;
 		else
-			info->buf_start = column;
+			nand->buf_start = column;
 
 		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
-			info->ndcb0 |= NDCB0_CMD_TYPE(0)
+			nand->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
 		else
-			info->ndcb0 |= NDCB0_CMD_TYPE(0)
+			nand->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| NDCB0_DBC
 					| addr_cycle
 					| cmd;
@@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
 		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
-			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
+			nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
-			info->ndcb2 = 0;
+			nand->ndcb2 = 0;
 		} else {
-			info->ndcb1 = ((page_addr & 0xFFFF) << 16)
+			nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
 					| (column & 0xFFFF);
 
 			if (page_addr & 0xFF0000)
-				info->ndcb2 = (page_addr & 0xFF0000) >> 16;
+				nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
 			else
-				info->ndcb2 = 0;
+				nand->ndcb2 = 0;
 		}
 
-		info->buf_count = mtd->writesize + mtd->oobsize;
-		memset(info->data_buff, 0xFF, info->buf_count);
+		nand->buf_count = mtd->writesize + mtd->oobsize;
+		memset(nand->data_buff, 0xFF, nand->buf_count);
 
 		break;
 
 	case NAND_CMD_PAGEPROG:
-		if (is_buf_blank(info->data_buff,
+		if (is_buf_blank(nand->data_buff,
 					(mtd->writesize + mtd->oobsize))) {
 			exec_cmd = 0;
 			break;
 		}
 
 		cmd = info->cmdset->program;
-		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
+		nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
 				| NDCB0_DBC
@@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_READID:
 		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
-		info->ndcb0 |= NDCB0_CMD_TYPE(3)
+		nand->buf_count = info->read_id_bytes;
+		nand->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
 
-		info->data_size = 8;
+		nand->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
 		cmd = info->cmdset->read_status;
-		info->buf_count = 1;
-		info->ndcb0 |= NDCB0_CMD_TYPE(4)
+		nand->buf_count = 1;
+		nand->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
 
-		info->data_size = 8;
+		nand->data_size = 8;
 		break;
 
 	case NAND_CMD_ERASE1:
 		cmd = info->cmdset->erase;
-		info->ndcb0 |= NDCB0_CMD_TYPE(2)
+		nand->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
 				| NDCB0_DBC
 				| cmd;
-		info->ndcb1 = page_addr;
-		info->ndcb2 = 0;
+		nand->ndcb1 = page_addr;
+		nand->ndcb2 = 0;
 
 		break;
 	case NAND_CMD_RESET:
 		cmd = info->cmdset->reset;
-		info->ndcb0 |= NDCB0_CMD_TYPE(5)
+		nand->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
 		break;
@@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 	int ret, exec_cmd;
 
 	/*
@@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (info->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
-	exec_cmd = prepare_command_pool(info, command, column, page_addr);
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (nand->chip_select != info->chip_select) {
+		nand->chip_select = info->chip_select;
+		nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
+		nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
+	}
+
+	nand->state = STATE_PREPARED;
+	exec_cmd = prepare_command_pool(nand, command, column, page_addr);
 	if (exec_cmd) {
-		init_completion(&info->cmd_complete);
-		pxa3xx_nand_start(info);
+		init_completion(&nand->cmd_complete);
+		pxa3xx_nand_start(nand);
 
-		ret = wait_for_completion_timeout(&info->cmd_complete,
+		ret = wait_for_completion_timeout(&nand->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
 			printk(KERN_ERR "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
-			pxa3xx_nand_stop(info);
+			pxa3xx_nand_stop(nand);
 		}
-		info->state = STATE_IDLE;
 	}
+	nand->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 
-	if (info->retcode == ERR_SBERR) {
+	if (nand->retcode == ERR_SBERR) {
 		switch (info->use_ecc) {
 		case 1:
 			mtd->ecc_stats.corrected++;
@@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		default:
 			break;
 		}
-	} else if (info->retcode == ERR_DBERR) {
+	} else if (nand->retcode == ERR_DBERR) {
 		/*
 		 * for blank page (all 0xff), HW will calculate its ECC as
 		 * 0, which is different from the ECC information within
 		 * OOB, ignore such double bit errors
 		 */
 		if (is_buf_blank(buf, mtd->writesize))
-			info->retcode = ERR_NONE;
+			nand->retcode = ERR_NONE;
 		else
 			mtd->ecc_stats.failed++;
 	}
@@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 	char retval = 0xFF;
 
-	if (info->buf_start < info->buf_count)
+	if (nand->buf_start < nand->buf_count)
 		/* Has just send a new command? */
-		retval = info->data_buff[info->buf_start++];
+		retval = nand->data_buff[nand->buf_start++];
 
 	return retval;
 }
@@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 	u16 retval = 0xFFFF;
 
-	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
-		retval = *((u16 *)(info->data_buff+info->buf_start));
-		info->buf_start += 2;
+	if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
+		retval = *((u16 *)(nand->data_buff+nand->buf_start));
+		nand->buf_start += 2;
 	}
 	return retval;
 }
@@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
-	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
+	struct pxa3xx_nand *nand = info->nand_data;
+	int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
 
-	memcpy(buf, info->data_buff + info->buf_start, real_len);
-	info->buf_start += real_len;
+	memcpy(buf, nand->data_buff + nand->buf_start, real_len);
+	nand->buf_start += real_len;
 }
 
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
-	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
+	struct pxa3xx_nand *nand = info->nand_data;
+	int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
 
-	memcpy(info->data_buff + info->buf_start, buf, real_len);
-	info->buf_start += real_len;
+	memcpy(nand->data_buff + nand->buf_start, buf, real_len);
+	nand->buf_start += real_len;
 }
 
 static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
@@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
-		if (info->retcode == ERR_NONE)
+		if (nand->retcode == ERR_NONE)
 			return 0;
 		else {
 			/*
@@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 				    const struct pxa3xx_nand_flash *f)
 {
-	struct platform_device *pdev = info->pdev;
+	struct pxa3xx_nand *nand = info->nand_data;
+	struct platform_device *pdev = nand->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
@@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
 	info->reg_ndcr = ndcr;
+	info->use_ecc = 1;
 
 	pxa3xx_nand_set_timing(info, f->timing);
 	return 0;
@@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	uint32_t ndcr = nand_readl(info, NDCR);
+	struct pxa3xx_nand *nand = info->nand_data;
+	uint32_t ndcr = nand_readl(nand, NDCR);
+
+	if (info->chip_select > 0) {
+		printk(KERN_ERR "We could not detect configure"
+				" if more than one cs is supported!!\n");
+		BUG();
+	}
 	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
 	/* set info fields needed to read id */
 	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
 	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
 	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
+	info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
 
 	return 0;
 }
@@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
  */
 #define MAX_BUFF_SIZE	PAGE_SIZE
 
-static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
+static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
 {
-	struct platform_device *pdev = info->pdev;
-	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
-
-	if (use_dma == 0) {
-		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
-		if (info->data_buff == NULL)
-			return -ENOMEM;
-		return 0;
-	}
-
-	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
-				&info->data_buff_phys, GFP_KERNEL);
-	if (info->data_buff == NULL) {
-		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
-		return -ENOMEM;
-	}
-
-	info->data_buff_size = MAX_BUFF_SIZE;
-	info->data_desc = (void *)info->data_buff + data_desc_offset;
-	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	struct pxa3xx_nand *nand;
+	struct mtd_info *mtd;
 
-	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
-				pxa3xx_nand_data_dma_irq, info);
-	if (info->data_dma_ch < 0) {
-		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-				info->data_buff, info->data_buff_phys);
-		return info->data_dma_ch;
-	}
+	if (!info)
+		return;
 
-	return 0;
+	nand = info->nand_data;
+	mtd = get_mtd_by_info(info);
+	kfree(mtd);
+	nand->info[cs] = NULL;
 }
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand *nand = info->nand_data;
+	struct mtd_info *mtd = get_mtd_by_info(info);
 	struct nand_chip *chip = mtd->priv;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
+		return 0;
 	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
-	if (info->is_ready)
+	if (nand->is_ready)
 		return 1;
 	else
 		return 0;
@@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
-	struct platform_device *pdev = info->pdev;
+	struct pxa3xx_nand *nand = info->nand_data;
+	struct platform_device *pdev = nand->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
 	const struct pxa3xx_nand_flash *f = NULL;
@@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	uint64_t chipsize;
 	int i, ret, num;
 
+	nand->chip_select = info->chip_select;
 	if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
 	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		free_cs_resource(info, nand->chip_select);
+		printk(KERN_INFO "There is no nand chip on cs %d!\n",
+				nand->chip_select);
 
 		return -EINVAL;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
-	id = *((uint16_t *)(info->data_buff));
+	id = *((uint16_t *)(nand->data_buff));
 	if (id != 0)
 		printk(KERN_INFO "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
-
+		free_cs_resource(info, nand->chip_select);
 		return -EINVAL;
 	}
 
@@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
+		free_cs_resource(info, nand->chip_select);
 		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	if (pxa3xx_nand_config_flash(info, f)) {
+		printk(KERN_ERR "ERROR! Configure failed\n");
+		return -EINVAL;
+	}
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -950,13 +977,13 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
+	nand->oob_buff = nand->data_buff + mtd->writesize;
 	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
-	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
 		info->row_addr_cycles = 3;
 	else
 		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
+	mtd->name = mtd_names[nand->chip_select];
 	chip->ecc.mode = NAND_ECC_HW;
 	chip->ecc.size = info->page_size;
 
@@ -967,51 +994,33 @@ KEEP_CONFIG:
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
+	struct pxa3xx_nand *nand;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
+	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
-			GFP_KERNEL);
-	if (!mtd) {
+	pdata = pdev->dev.platform_data;
+	nand = kzalloc(sizeof(struct mtd_info)
+			+ sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
+	if (!nand) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
-
-	spin_lock_init(&chip->controller->lock);
-	init_waitqueue_head(&chip->controller->wq);
-	info->clk = clk_get(&pdev->dev, NULL);
-	if (IS_ERR(info->clk)) {
+	nand->pdev = pdev;
+	nand->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(nand->clk)) {
 		dev_err(&pdev->dev, "failed to get nand clock\n");
-		ret = PTR_ERR(info->clk);
-		goto fail_free_mtd;
+		ret = PTR_ERR(nand->clk);
+		goto fail_alloc;
 	}
-	clk_enable(info->clk);
+	clk_enable(nand->clk);
 
 	r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 	if (r == NULL) {
@@ -1019,7 +1028,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 		ret = -ENXIO;
 		goto fail_put_clk;
 	}
-	info->drcmr_dat = r->start;
+	nand->drcmr_dat = r->start;
 
 	r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 	if (r == NULL) {
@@ -1027,7 +1036,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 		ret = -ENXIO;
 		goto fail_put_clk;
 	}
-	info->drcmr_cmd = r->start;
+	nand->drcmr_cmd = r->start;
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0) {
@@ -1050,81 +1059,147 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 		goto fail_put_clk;
 	}
 
-	info->mmio_base = ioremap(r->start, resource_size(r));
-	if (info->mmio_base == NULL) {
+	nand->mmio_base = ioremap(r->start, resource_size(r));
+	if (nand->mmio_base == NULL) {
 		dev_err(&pdev->dev, "ioremap() failed\n");
 		ret = -ENODEV;
 		goto fail_free_res;
 	}
-	info->mmio_phys = r->start;
-
-	ret = pxa3xx_nand_init_buff(info);
-	if (ret)
-		goto fail_free_io;
+	nand->mmio_phys = r->start;
 
 	/* initialize all interrupts to be disabled */
-	disable_int(info, NDSR_MASK);
+	disable_int(nand, NDSR_MASK);
 
 	ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED,
-			  pdev->name, info);
+			  pdev->name, nand);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to request IRQ\n");
-		goto fail_free_buf;
+		ret = -ENXIO;
+		goto fail_free_io;
+	}
+
+	platform_set_drvdata(pdev, nand);
+
+	spin_lock_init(&nand->controller.lock);
+	init_waitqueue_head(&nand->controller.wq);
+	for (cs = 0; cs < pdata->cs_num; cs++) {
+		mtd = kzalloc(sizeof(struct mtd_info)
+				+ sizeof(struct pxa3xx_nand_info),
+				GFP_KERNEL);
+		if (!mtd) {
+			dev_err(&pdev->dev, "failed to allocate memory\n");
+			ret = -ENOMEM;
+			goto fail_free_irq;
+		}
+
+		info = (struct pxa3xx_nand_info *)(&mtd[1]);
+		info->nand_data = nand;
+		info->chip_select = cs;
+		mtd->priv = info;
+		mtd->owner = THIS_MODULE;
+		nand->info[cs] = info;
+
+		chip = (struct nand_chip *)(&mtd[1]);
+		chip->controller        = &nand->controller;
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
 	}
 
-	platform_set_drvdata(pdev, info);
+	if (use_dma == 0) {
+		nand->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
+		if (nand->data_buff == NULL) {
+			ret = -ENOMEM;
+			goto fail_free_buf;
+		}
+		goto success_exit;
+	}
 
-	return info;
+	nand->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
+			&nand->data_buff_phys, GFP_KERNEL);
+	if (nand->data_buff == NULL) {
+		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
+		ret = -ENOMEM;
+		goto fail_free_buf;
+	}
 
+	nand->data_desc = (void *)nand->data_buff + data_desc_offset;
+	nand->data_desc_addr = nand->data_buff_phys + data_desc_offset;
+	nand->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
+			pxa3xx_nand_data_dma_irq, nand);
+	if (nand->data_dma_ch < 0) {
+		dev_err(&pdev->dev, "failed to request data dma\n");
+		ret = -ENXIO;
+		goto fail_free_dma_buf;
+	}
+success_exit:
+	return 0;
+
+fail_free_dma_buf:
+	dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
+			nand->data_buff, nand->data_buff_phys);
 fail_free_buf:
-	free_irq(irq, info);
-	if (use_dma) {
-		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-			info->data_buff, info->data_buff_phys);
-	} else
-		kfree(info->data_buff);
+	for (cs = 0; cs < pdata->cs_num; cs++) {
+		info = nand->info[cs];
+		free_cs_resource(info, cs);
+	}
+fail_free_irq:
+	free_irq(irq, nand);
 fail_free_io:
-	iounmap(info->mmio_base);
+	iounmap(nand->mmio_base);
 fail_free_res:
 	release_mem_region(r->start, resource_size(r));
 fail_put_clk:
-	clk_disable(info->clk);
-	clk_put(info->clk);
-fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	clk_disable(nand->clk);
+	clk_put(nand->clk);
+fail_alloc:
+	kfree(nand);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
-	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand *nand = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
+	struct pxa3xx_nand_info *info;
+	struct mtd_info *mtd;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	platform_set_drvdata(pdev, NULL);
+	pdata = pdev->dev.platform_data;
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq >= 0)
-		free_irq(irq, info);
+		free_irq(irq, nand);
 	if (use_dma) {
-		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
-				info->data_buff, info->data_buff_phys);
+		pxa_free_dma(nand->data_dma_ch);
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
+				nand->data_buff, nand->data_buff_phys);
 	} else
-		kfree(info->data_buff);
+		kfree(nand->data_buff);
 
-	iounmap(info->mmio_base);
+	iounmap(nand->mmio_base);
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	clk_disable(info->clk);
-	clk_put(info->clk);
+	clk_disable(nand->clk);
+	clk_put(nand->clk);
 
-	if (mtd) {
+	for (cs = 0; cs < pdata->cs_num; cs++) {
+		info = nand->info[cs];
+		if (!info)
+			continue;
+		mtd = get_mtd_by_info(info);
 		mtd_device_unregister(mtd);
-		kfree(mtd);
+		free_cs_resource(info, cs);
 	}
 	return 0;
 }
@@ -1134,34 +1209,54 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 
+	struct pxa3xx_nand *nand;
+	struct mtd_info *mtd;
+	int cs, ret, nr_parts, probe_success;
+
+	probe_success = 0;
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
 		dev_err(&pdev->dev, "no platform data defined\n");
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
+	ret = alloc_nand_resource(pdev);
+	if (ret)
 		return -ENOMEM;
 
-	if (pxa3xx_nand_scan(info->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
-		pxa3xx_nand_remove(pdev);
-		return -ENODEV;
-	}
+	nand = platform_get_drvdata(pdev);
+	for (cs = 0; cs < pdata->cs_num; cs++) {
+		info = nand->info[cs];
+		mtd = get_mtd_by_info(info);
+		if (pxa3xx_nand_scan(mtd)) {
+			dev_err(&pdev->dev, "failed to scan nand\n");
+			continue;
+		}
+
+		ret = 0;
+		nr_parts = 0;
+		if (mtd_has_cmdlinepart()) {
+			const char *probes[] = { "cmdlinepart", NULL };
+			struct mtd_partition *parts;
 
-	if (mtd_has_cmdlinepart()) {
-		const char *probes[] = { "cmdlinepart", NULL };
-		struct mtd_partition *parts;
-		int nr_parts;
+			nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0);
 
-		nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0);
+			if (nr_parts)
+				ret = mtd_device_register(mtd, parts, nr_parts);
+		}
 
-		if (nr_parts)
-			return mtd_device_register(info->mtd, parts, nr_parts);
+		if (!nr_parts)
+			ret = mtd_device_register(mtd, pdata->parts[cs],
+					pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
 	}
 
-	return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
+	if (!probe_success) {
+		pxa3xx_nand_remove(pdev);
+		return -ENODEV;
+	} else
+		return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 	struct mtd_info *mtd = info->mtd;
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
+	nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-08 16:00 ` Lei Wen
@ 2011-06-22 11:39   ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-06-22 11:39 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Haojian Zhuang,
	linux-mtd, linux-arm-kernel

On Wed, Jun 22, 2011 at 5:17 AM, Lei Wen <leiwen@marvell.com> wrote:
> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
>
> Update platform driver to support this feature.
>
> Another 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.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>  9 files changed, 444 insertions(+), 338 deletions(-)

This patch doesn't apply for me on top of Linus' master branch. Which
tree are you based on currently?

[...]

> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index 30689cc..259b8d5 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -92,11 +92,13 @@
>  #define NDCB0_ADDR_CYC_SHIFT   (16)
>
>  /* macros for registers read/write */
> -#define nand_writel(info, off, val)    \
> -       __raw_writel((val), (info)->mmio_base + (off))
> +#define nand_writel(nand, off, val)    \
> +       __raw_writel((val), (nand)->mmio_base + (off))
>
> -#define nand_readl(info, off)          \
> -       __raw_readl((info)->mmio_base + (off))
> +#define nand_readl(nand, off)          \
> +       __raw_readl((nand)->mmio_base + (off))
> +#define get_mtd_by_info(info)          \
> +       (struct mtd_info *)((void *)info - sizeof(struct mtd_info))
>
>  /* error code and state */
>  enum {
> @@ -110,6 +112,7 @@ enum {
>
>  enum {
>        STATE_IDLE = 0,
> +       STATE_PREPARED,
>        STATE_CMD_HANDLE,
>        STATE_DMA_READING,
>        STATE_DMA_WRITING,
> @@ -123,63 +126,63 @@ enum {
>  struct pxa3xx_nand_info {
>        struct nand_chip        nand_chip;
>
> -       struct nand_hw_control  controller;
> -       struct platform_device   *pdev;
>        struct pxa3xx_nand_cmdset *cmdset;
> +       /* page size of attached chip */
> +       uint16_t                page_size;
> +       uint8_t                 chip_select;
> +       uint8_t                 use_ecc;
> +
> +       /* calculated from pxa3xx_nand_flash data */
> +       uint8_t                 col_addr_cycles;
> +       uint8_t                 row_addr_cycles;
> +       uint8_t                 read_id_bytes;
> +
> +       /* cached register value */
> +       uint32_t                reg_ndcr;
> +       uint32_t                ndtr0cs0;
> +       uint32_t                ndtr1cs0;
>
> +       void                    *nand_data;
> +};
> +
> +struct pxa3xx_nand {
>        struct clk              *clk;
>        void __iomem            *mmio_base;
>        unsigned long           mmio_phys;
> +       struct nand_hw_control  controller;
> +       struct completion       cmd_complete;
> +       struct platform_device   *pdev;
>
> -       unsigned int            buf_start;
> -       unsigned int            buf_count;
> -
> -       struct mtd_info         *mtd;
>        /* DMA information */
>        int                     drcmr_dat;
>        int                     drcmr_cmd;
> -
> -       unsigned char           *data_buff;
> -       unsigned char           *oob_buff;
> -       dma_addr_t              data_buff_phys;
> -       size_t                  data_buff_size;
>        int                     data_dma_ch;
> -       struct pxa_dma_desc     *data_desc;
> +       dma_addr_t              data_buff_phys;
>        dma_addr_t              data_desc_addr;
> +       struct pxa_dma_desc     *data_desc;
>
> -       uint32_t                reg_ndcr;
> -
> -       /* saved column/page_addr during CMD_SEQIN */
> -       int                     seqin_column;
> -       int                     seqin_page_addr;
> +       struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
> +       uint32_t                command;
> +       uint16_t                data_size;      /* data size in FIFO */
> +       uint16_t                oob_size;
> +       unsigned char           *data_buff;
> +       unsigned char           *oob_buff;
> +       uint32_t                buf_start;
> +       uint32_t                buf_count;
>
>        /* relate to the command */
>        unsigned int            state;
> -
> +       uint8_t                 chip_select;
>        int                     use_ecc;        /* use HW ECC ? */
>        int                     use_dma;        /* use DMA ? */
>        int                     is_ready;
> -
> -       unsigned int            page_size;      /* page size of attached chip */
> -       unsigned int            data_size;      /* data size in FIFO */
>        int                     retcode;
> -       struct completion       cmd_complete;
>
>        /* generated NDCBx register values */
> +       uint8_t                 total_cmds;
>        uint32_t                ndcb0;
>        uint32_t                ndcb1;
>        uint32_t                ndcb2;
> -
> -       /* timing calcuted from setting */
> -       uint32_t                ndtr0cs0;
> -       uint32_t                ndtr1cs0;
> -
> -       /* calculated from pxa3xx_nand_flash data */
> -       size_t          oob_size;
> -       size_t          read_id_bytes;
> -
> -       unsigned int    col_addr_cycles;
> -       unsigned int    row_addr_cycles;
>  };
>
>  static int use_dma = 1;
> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>  /* Define a default flash type setting serve as flash detecting only */
>  #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>
> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>
>  #define NDTR0_tCH(c)   (min((c), 7) << 19)
>  #define NDTR0_tCS(c)   (min((c), 7) << 16)
> @@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>  static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>                                   const struct pxa3xx_nand_timing *t)
>  {
> -       unsigned long nand_clk = clk_get_rate(info->clk);
> +       struct pxa3xx_nand *nand = info->nand_data;
> +       unsigned long nand_clk;
>        uint32_t ndtr0, ndtr1;
>
> +       nand_clk = clk_get_rate(nand->clk);
>        ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
>                NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
>                NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
> @@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>
>        info->ndtr0cs0 = ndtr0;
>        info->ndtr1cs0 = ndtr1;
> -       nand_writel(info, NDTR0CS0, ndtr0);
> -       nand_writel(info, NDTR1CS0, ndtr1);
> +       nand_writel(nand, NDTR0CS0, ndtr0);
> +       nand_writel(nand, NDTR1CS0, ndtr1);
>  }
>
>  static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>  {
> +       struct pxa3xx_nand *nand = info->nand_data;
>        int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
>
> -       info->data_size = info->page_size;
> +       nand->data_size = info->page_size;
>        if (!oob_enable) {
> -               info->oob_size = 0;
> +               nand->oob_size = 0;
>                return;
>        }
>
>        switch (info->page_size) {
>        case 2048:
> -               info->oob_size = (info->use_ecc) ? 40 : 64;
> +               nand->oob_size = (info->use_ecc) ? 40 : 64;
>                break;
>        case 512:
> -               info->oob_size = (info->use_ecc) ? 8 : 16;
> +               nand->oob_size = (info->use_ecc) ? 8 : 16;
>                break;
>        }
>  }
> @@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>  * 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_info *info)
> +static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
>  {
> +       struct pxa3xx_nand_info *info;
>        uint32_t ndcr;
>
> +       info = nand->info[nand->chip_select];
>        ndcr = info->reg_ndcr;
> -       ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
> -       ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
> +       ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
> +       ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
>        ndcr |= NDCR_ND_RUN;
>
>        /* clear status bits and run */
> -       nand_writel(info, NDCR, 0);
> -       nand_writel(info, NDSR, NDSR_MASK);
> -       nand_writel(info, NDCR, ndcr);
> +       nand_writel(nand, NDCR, 0);
> +       nand_writel(nand, NDSR, NDSR_MASK);
> +       nand_writel(nand, NDCR, ndcr);
>  }
>
> -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
> +static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
>  {
>        uint32_t ndcr;
>        int timeout = NAND_STOP_DELAY;
>
>        /* wait RUN bit in NDCR become 0 */
> -       ndcr = nand_readl(info, NDCR);
> +       ndcr = nand_readl(nand, NDCR);
>        while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
> -               ndcr = nand_readl(info, NDCR);
> +               ndcr = nand_readl(nand, NDCR);
>                udelay(1);
>        }
>
>        if (timeout <= 0) {
>                ndcr &= ~NDCR_ND_RUN;
> -               nand_writel(info, NDCR, ndcr);
> +               nand_writel(nand, NDCR, ndcr);
>        }
>        /* clear status bits */
> -       nand_writel(info, NDSR, NDSR_MASK);
> +       nand_writel(nand, NDSR, NDSR_MASK);
>  }
>
> -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> +static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>  {
>        uint32_t ndcr;
>
> -       ndcr = nand_readl(info, NDCR);
> -       nand_writel(info, NDCR, ndcr & ~int_mask);
> +       ndcr = nand_readl(nand, NDCR);
> +       nand_writel(nand, NDCR, ndcr & ~int_mask);
>  }
>
> -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>  {
>        uint32_t ndcr;
>
> -       ndcr = nand_readl(info, NDCR);
> -       nand_writel(info, NDCR, ndcr | int_mask);
> +       ndcr = nand_readl(nand, NDCR);
> +       nand_writel(nand, NDCR, ndcr | int_mask);
>  }
>
> -static void handle_data_pio(struct pxa3xx_nand_info *info)
> +static void handle_data_pio(struct pxa3xx_nand *nand)
>  {
> -       switch (info->state) {
> +       switch (nand->state) {
>        case STATE_PIO_WRITING:
> -               __raw_writesl(info->mmio_base + NDDB, info->data_buff,
> -                               DIV_ROUND_UP(info->data_size, 4));
> -               if (info->oob_size > 0)
> -                       __raw_writesl(info->mmio_base + NDDB, info->oob_buff,
> -                                       DIV_ROUND_UP(info->oob_size, 4));
> +               __raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
> +                               DIV_ROUND_UP(nand->data_size, 4));
> +               if (nand->oob_size > 0)
> +                       __raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
> +                                       DIV_ROUND_UP(nand->oob_size, 4));
>                break;
>        case STATE_PIO_READING:
> -               __raw_readsl(info->mmio_base + NDDB, info->data_buff,
> -                               DIV_ROUND_UP(info->data_size, 4));
> -               if (info->oob_size > 0)
> -                       __raw_readsl(info->mmio_base + NDDB, info->oob_buff,
> -                                       DIV_ROUND_UP(info->oob_size, 4));
> +               __raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
> +                               DIV_ROUND_UP(nand->data_size, 4));
> +               if (nand->oob_size > 0)
> +                       __raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
> +                                       DIV_ROUND_UP(nand->oob_size, 4));
>                break;
>        default:
>                printk(KERN_ERR "%s: invalid state %d\n", __func__,
> -                               info->state);
> +                               nand->state);

Can't you use dev_err() here?

>                BUG();

Is crashing the entire kernel really necessary here?

>        }
>  }
>
> -static void start_data_dma(struct pxa3xx_nand_info *info)
> +static void start_data_dma(struct pxa3xx_nand *nand)
>  {
> -       struct pxa_dma_desc *desc = info->data_desc;
> -       int dma_len = ALIGN(info->data_size + info->oob_size, 32);
> +       struct pxa_dma_desc *desc = nand->data_desc;
> +       int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
>
>        desc->ddadr = DDADR_STOP;
>        desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
>
> -       switch (info->state) {
> +       switch (nand->state) {
>        case STATE_DMA_WRITING:
> -               desc->dsadr = info->data_buff_phys;
> -               desc->dtadr = info->mmio_phys + NDDB;
> +               desc->dsadr = nand->data_buff_phys;
> +               desc->dtadr = nand->mmio_phys + NDDB;
>                desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
>                break;
>        case STATE_DMA_READING:
> -               desc->dtadr = info->data_buff_phys;
> -               desc->dsadr = info->mmio_phys + NDDB;
> +               desc->dtadr = nand->data_buff_phys;
> +               desc->dsadr = nand->mmio_phys + NDDB;
>                desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
>                break;
>        default:
>                printk(KERN_ERR "%s: invalid state %d\n", __func__,
> -                               info->state);
> +                               nand->state);
>                BUG();
>        }
>
> -       DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
> -       DDADR(info->data_dma_ch) = info->data_desc_addr;
> -       DCSR(info->data_dma_ch) |= DCSR_RUN;
> +       DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
> +       DDADR(nand->data_dma_ch) = nand->data_desc_addr;
> +       DCSR(nand->data_dma_ch) |= DCSR_RUN;
>  }
>
>  static void pxa3xx_nand_data_dma_irq(int channel, void *data)
>  {
> -       struct pxa3xx_nand_info *info = data;
> +       struct pxa3xx_nand *nand = data;
>        uint32_t dcsr;
>
>        dcsr = DCSR(channel);
>        DCSR(channel) = dcsr;
>
>        if (dcsr & DCSR_BUSERR) {
> -               info->retcode = ERR_DMABUSERR;
> +               nand->retcode = ERR_DMABUSERR;
>        }
>
> -       info->state = STATE_DMA_DONE;
> -       enable_int(info, NDCR_INT_MASK);
> -       nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
> +       nand->state = STATE_DMA_DONE;
> +       enable_int(nand, NDCR_INT_MASK);
> +       nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>  }
>
>  static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>  {
> -       struct pxa3xx_nand_info *info = devid;
> -       unsigned int status, is_completed = 0;
> +       struct pxa3xx_nand *nand = devid;
> +       struct pxa3xx_nand_info *info;
> +       unsigned int status, is_completed = 0, cs;
> +       unsigned int ready, cmd_done, page_done, badblock_detect;
>
> -       status = nand_readl(info, NDSR);
> +       cs              = nand->chip_select;
> +       ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
> +       cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
> +       page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
> +       badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
> +       info            = nand->info[cs];
>
> +       status = nand_readl(nand, NDSR);
>        if (status & NDSR_DBERR)
> -               info->retcode = ERR_DBERR;
> +               nand->retcode = ERR_DBERR;
>        if (status & NDSR_SBERR)
> -               info->retcode = ERR_SBERR;
> +               nand->retcode = ERR_SBERR;
>        if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
>                /* whether use dma to transfer data */
> -               if (info->use_dma) {
> -                       disable_int(info, NDCR_INT_MASK);
> -                       info->state = (status & NDSR_RDDREQ) ?
> +               if (nand->use_dma) {
> +                       disable_int(nand, NDCR_INT_MASK);
> +                       nand->state = (status & NDSR_RDDREQ) ?
>                                      STATE_DMA_READING : STATE_DMA_WRITING;
> -                       start_data_dma(info);
> +                       start_data_dma(nand);
>                        goto NORMAL_IRQ_EXIT;
>                } else {
> -                       info->state = (status & NDSR_RDDREQ) ?
> +                       nand->state = (status & NDSR_RDDREQ) ?
>                                      STATE_PIO_READING : STATE_PIO_WRITING;
> -                       handle_data_pio(info);
> +                       handle_data_pio(nand);
>                }
>        }
> -       if (status & NDSR_CS0_CMDD) {
> -               info->state = STATE_CMD_DONE;
> +       if (status & cmd_done) {
> +               nand->state = STATE_CMD_DONE;
>                is_completed = 1;
>        }
> -       if (status & NDSR_FLASH_RDY) {
> -               info->is_ready = 1;
> -               info->state = STATE_READY;
> +       if (status & ready) {
> +               nand->is_ready = 1;
> +               nand->state = STATE_READY;
>        }
>
>        if (status & NDSR_WRCMDREQ) {
> -               nand_writel(info, NDSR, NDSR_WRCMDREQ);
> +               nand_writel(nand, NDSR, NDSR_WRCMDREQ);
>                status &= ~NDSR_WRCMDREQ;
> -               info->state = STATE_CMD_HANDLE;
> -               nand_writel(info, NDCB0, info->ndcb0);
> -               nand_writel(info, NDCB0, info->ndcb1);
> -               nand_writel(info, NDCB0, info->ndcb2);
> +               nand->state = STATE_CMD_HANDLE;
> +               nand_writel(nand, NDCB0, nand->ndcb0);
> +               nand_writel(nand, NDCB0, nand->ndcb1);
> +               nand_writel(nand, NDCB0, nand->ndcb2);
>        }
>
>        /* clear NDSR to let the controller exit the IRQ */
> -       nand_writel(info, NDSR, status);
> +       nand_writel(nand, NDSR, status);
>        if (is_completed)
> -               complete(&info->cmd_complete);
> +               complete(&nand->cmd_complete);
>  NORMAL_IRQ_EXIT:
>        return IRQ_HANDLED;
>  }
>
> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
> -{
> -       struct pxa3xx_nand_info *info = mtd->priv;
> -       return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
> -}
> -
>  static inline int is_buf_blank(uint8_t *buf, size_t len)
>  {
>        for (; len > 0; len--)
> @@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
>        return 1;
>  }
>
> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>                uint16_t column, int page_addr)
>  {
>        uint16_t cmd;
>        int addr_cycle, exec_cmd, ndcb0;
> -       struct mtd_info *mtd = info->mtd;
> +       struct mtd_info *mtd;
> +       struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>
> -       ndcb0 = 0;
> +       mtd = get_mtd_by_info(info);
> +       ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
>        addr_cycle = 0;
>        exec_cmd = 1;
>
>        /* reset data and oob column point to handle data */
> -       info->buf_start         = 0;
> -       info->buf_count         = 0;
> -       info->oob_size          = 0;
> -       info->use_ecc           = 0;
> -       info->is_ready          = 0;
> -       info->retcode           = ERR_NONE;
> +       nand->buf_start         = 0;
> +       nand->buf_count         = 0;
> +       nand->oob_size          = 0;
> +       nand->use_ecc           = 0;
> +       nand->is_ready          = 0;
> +       nand->retcode           = ERR_NONE;
> +       nand->data_size         = 0;
> +       nand->use_dma           = 0;
> +       nand->command           = command;
>
>        switch (command) {
>        case NAND_CMD_READ0:
>        case NAND_CMD_PAGEPROG:
> -               info->use_ecc = 1;
> +               nand->use_ecc = 1;
>        case NAND_CMD_READOOB:
>                pxa3xx_set_datasize(info);
> +               nand->oob_buff = nand->data_buff + nand->data_size;
> +               nand->use_dma = use_dma;
>                break;
>        case NAND_CMD_SEQIN:
>                exec_cmd = 0;
>                break;
>        default:
> -               info->ndcb1 = 0;
> -               info->ndcb2 = 0;
> +               nand->ndcb1 = 0;
> +               nand->ndcb2 = 0;
>                break;
>        }
>
> -       info->ndcb0 = ndcb0;
> +       nand->ndcb0 = ndcb0;
>        addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
>                                    + info->col_addr_cycles);
>
> @@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>        case NAND_CMD_READ0:
>                cmd = info->cmdset->read1;
>                if (command == NAND_CMD_READOOB)
> -                       info->buf_start = mtd->writesize + column;
> +                       nand->buf_start = mtd->writesize + column;
>                else
> -                       info->buf_start = column;
> +                       nand->buf_start = column;
>
>                if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
> -                       info->ndcb0 |= NDCB0_CMD_TYPE(0)
> +                       nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>                                        | addr_cycle
>                                        | (cmd & NDCB0_CMD1_MASK);
>                else
> -                       info->ndcb0 |= NDCB0_CMD_TYPE(0)
> +                       nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>                                        | NDCB0_DBC
>                                        | addr_cycle
>                                        | cmd;
> @@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>        case NAND_CMD_SEQIN:
>                /* small page addr setting */
>                if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
> -                       info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
> +                       nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>                                        | (column & 0xFF);
>
> -                       info->ndcb2 = 0;
> +                       nand->ndcb2 = 0;
>                } else {
> -                       info->ndcb1 = ((page_addr & 0xFFFF) << 16)
> +                       nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
>                                        | (column & 0xFFFF);
>
>                        if (page_addr & 0xFF0000)
> -                               info->ndcb2 = (page_addr & 0xFF0000) >> 16;
> +                               nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
>                        else
> -                               info->ndcb2 = 0;
> +                               nand->ndcb2 = 0;
>                }
>
> -               info->buf_count = mtd->writesize + mtd->oobsize;
> -               memset(info->data_buff, 0xFF, info->buf_count);
> +               nand->buf_count = mtd->writesize + mtd->oobsize;
> +               memset(nand->data_buff, 0xFF, nand->buf_count);
>
>                break;
>
>        case NAND_CMD_PAGEPROG:
> -               if (is_buf_blank(info->data_buff,
> +               if (is_buf_blank(nand->data_buff,
>                                        (mtd->writesize + mtd->oobsize))) {
>                        exec_cmd = 0;
>                        break;
>                }
>
>                cmd = info->cmdset->program;
> -               info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> +               nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>                                | NDCB0_AUTO_RS
>                                | NDCB0_ST_ROW_EN
>                                | NDCB0_DBC
> @@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>
>        case NAND_CMD_READID:
>                cmd = info->cmdset->read_id;
> -               info->buf_count = info->read_id_bytes;
> -               info->ndcb0 |= NDCB0_CMD_TYPE(3)
> +               nand->buf_count = info->read_id_bytes;
> +               nand->ndcb0 |= NDCB0_CMD_TYPE(3)
>                                | NDCB0_ADDR_CYC(1)
>                                | cmd;
>
> -               info->data_size = 8;
> +               nand->data_size = 8;
>                break;
>        case NAND_CMD_STATUS:
>                cmd = info->cmdset->read_status;
> -               info->buf_count = 1;
> -               info->ndcb0 |= NDCB0_CMD_TYPE(4)
> +               nand->buf_count = 1;
> +               nand->ndcb0 |= NDCB0_CMD_TYPE(4)
>                                | NDCB0_ADDR_CYC(1)
>                                | cmd;
>
> -               info->data_size = 8;
> +               nand->data_size = 8;
>                break;
>
>        case NAND_CMD_ERASE1:
>                cmd = info->cmdset->erase;
> -               info->ndcb0 |= NDCB0_CMD_TYPE(2)
> +               nand->ndcb0 |= NDCB0_CMD_TYPE(2)
>                                | NDCB0_AUTO_RS
>                                | NDCB0_ADDR_CYC(3)
>                                | NDCB0_DBC
>                                | cmd;
> -               info->ndcb1 = page_addr;
> -               info->ndcb2 = 0;
> +               nand->ndcb1 = page_addr;
> +               nand->ndcb2 = 0;
>
>                break;
>        case NAND_CMD_RESET:
>                cmd = info->cmdset->reset;
> -               info->ndcb0 |= NDCB0_CMD_TYPE(5)
> +               nand->ndcb0 |= NDCB0_CMD_TYPE(5)
>                                | cmd;
>
>                break;
> @@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>                                int column, int page_addr)
>  {
>        struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand *nand = info->nand_data;
>        int ret, exec_cmd;
>
>        /*
> @@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>        if (info->reg_ndcr & NDCR_DWIDTH_M)
>                column /= 2;
>
> -       exec_cmd = prepare_command_pool(info, command, column, page_addr);
> +       /*
> +        * There may be different NAND chip hooked to
> +        * different chip select, so check whether
> +        * chip select has been changed, if yes, reset the timing
> +        */
> +       if (nand->chip_select != info->chip_select) {
> +               nand->chip_select = info->chip_select;
> +               nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
> +               nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
> +       }
> +
> +       nand->state = STATE_PREPARED;
> +       exec_cmd = prepare_command_pool(nand, command, column, page_addr);
>        if (exec_cmd) {
> -               init_completion(&info->cmd_complete);
> -               pxa3xx_nand_start(info);
> +               init_completion(&nand->cmd_complete);
> +               pxa3xx_nand_start(nand);
>
> -               ret = wait_for_completion_timeout(&info->cmd_complete,
> +               ret = wait_for_completion_timeout(&nand->cmd_complete,
>                                CHIP_DELAY_TIMEOUT);
>                if (!ret) {
>                        printk(KERN_ERR "Wait time out!!!\n");
>                        /* Stop State Machine for next command cycle */
> -                       pxa3xx_nand_stop(info);
> +                       pxa3xx_nand_stop(nand);
>                }
> -               info->state = STATE_IDLE;
>        }
> +       nand->state = STATE_IDLE;
>  }
>
>  static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
> @@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>                struct nand_chip *chip, uint8_t *buf, int page)
>  {
>        struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand *nand = info->nand_data;
>
>        chip->read_buf(mtd, buf, mtd->writesize);
>        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>
> -       if (info->retcode == ERR_SBERR) {
> +       if (nand->retcode == ERR_SBERR) {
>                switch (info->use_ecc) {
>                case 1:
>                        mtd->ecc_stats.corrected++;
> @@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>                default:
>                        break;
>                }
> -       } else if (info->retcode == ERR_DBERR) {
> +       } else if (nand->retcode == ERR_DBERR) {
>                /*
>                 * for blank page (all 0xff), HW will calculate its ECC as
>                 * 0, which is different from the ECC information within
>                 * OOB, ignore such double bit errors
>                 */
>                if (is_buf_blank(buf, mtd->writesize))
> -                       info->retcode = ERR_NONE;
> +                       nand->retcode = ERR_NONE;
>                else
>                        mtd->ecc_stats.failed++;
>        }
> @@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>  static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>  {
>        struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand *nand = info->nand_data;
>        char retval = 0xFF;
>
> -       if (info->buf_start < info->buf_count)
> +       if (nand->buf_start < nand->buf_count)
>                /* Has just send a new command? */
> -               retval = info->data_buff[info->buf_start++];
> +               retval = nand->data_buff[nand->buf_start++];
>
>        return retval;
>  }
> @@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>  static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>  {
>        struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand *nand = info->nand_data;
>        u16 retval = 0xFFFF;
>
> -       if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
> -               retval = *((u16 *)(info->data_buff+info->buf_start));
> -               info->buf_start += 2;
> +       if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
> +               retval = *((u16 *)(nand->data_buff+nand->buf_start));
> +               nand->buf_start += 2;
>        }
>        return retval;
>  }
> @@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>  static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>  {
>        struct pxa3xx_nand_info *info = mtd->priv;
> -       int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
> +       struct pxa3xx_nand *nand = info->nand_data;
> +       int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>
> -       memcpy(buf, info->data_buff + info->buf_start, real_len);
> -       info->buf_start += real_len;
> +       memcpy(buf, nand->data_buff + nand->buf_start, real_len);
> +       nand->buf_start += real_len;
>  }
>
>  static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
>                const uint8_t *buf, int len)
>  {
>        struct pxa3xx_nand_info *info = mtd->priv;
> -       int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
> +       struct pxa3xx_nand *nand = info->nand_data;
> +       int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>
> -       memcpy(info->data_buff + info->buf_start, buf, real_len);
> -       info->buf_start += real_len;
> +       memcpy(nand->data_buff + nand->buf_start, buf, real_len);
> +       nand->buf_start += real_len;
>  }
>
>  static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
> @@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>  static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>  {
>        struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand *nand = info->nand_data;
>
>        /* pxa3xx_nand_send_command has waited for command complete */
>        if (this->state == FL_WRITING || this->state == FL_ERASING) {
> -               if (info->retcode == ERR_NONE)
> +               if (nand->retcode == ERR_NONE)
>                        return 0;
>                else {
>                        /*
> @@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>  static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>                                    const struct pxa3xx_nand_flash *f)
>  {
> -       struct platform_device *pdev = info->pdev;
> +       struct pxa3xx_nand *nand = info->nand_data;
> +       struct platform_device *pdev = nand->pdev;
>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>        uint32_t ndcr = 0x0; /* enable all interrupts */
>
> @@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>        ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>
>        info->reg_ndcr = ndcr;
> +       info->use_ecc = 1;
>
>        pxa3xx_nand_set_timing(info, f->timing);
>        return 0;
> @@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>
>  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>  {
> -       uint32_t ndcr = nand_readl(info, NDCR);
> +       struct pxa3xx_nand *nand = info->nand_data;
> +       uint32_t ndcr = nand_readl(nand, NDCR);
> +
> +       if (info->chip_select > 0) {
> +               printk(KERN_ERR "We could not detect configure"
> +                               " if more than one cs is supported!!\n");
> +               BUG();
> +       }
>        info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
>        /* set info fields needed to read id */
>        info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
>        info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
>        info->cmdset = &default_cmdset;
>
> -       info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> -       info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
> +       info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
> +       info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
>
>        return 0;
>  }
> @@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>  */
>  #define MAX_BUFF_SIZE  PAGE_SIZE
>
> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
>  {
> -       struct platform_device *pdev = info->pdev;
> -       int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
> -
> -       if (use_dma == 0) {
> -               info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> -               if (info->data_buff == NULL)
> -                       return -ENOMEM;
> -               return 0;
> -       }
> -
> -       info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> -                               &info->data_buff_phys, GFP_KERNEL);
> -       if (info->data_buff == NULL) {
> -               dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> -               return -ENOMEM;
> -       }
> -
> -       info->data_buff_size = MAX_BUFF_SIZE;
> -       info->data_desc = (void *)info->data_buff + data_desc_offset;
> -       info->data_desc_addr = info->data_buff_phys + data_desc_offset;
> +       struct pxa3xx_nand *nand;
> +       struct mtd_info *mtd;
>
> -       info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> -                               pxa3xx_nand_data_dma_irq, info);
> -       if (info->data_dma_ch < 0) {
> -               dev_err(&pdev->dev, "failed to request data dma\n");
> -               dma_free_coherent(&pdev->dev, info->data_buff_size,
> -                               info->data_buff, info->data_buff_phys);
> -               return info->data_dma_ch;
> -       }
> +       if (!info)
> +               return;
>
> -       return 0;
> +       nand = info->nand_data;
> +       mtd = get_mtd_by_info(info);
> +       kfree(mtd);
> +       nand->info[cs] = NULL;
>  }
>
>  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>  {
> -       struct mtd_info *mtd = info->mtd;
> +       struct pxa3xx_nand *nand = info->nand_data;
> +       struct mtd_info *mtd = get_mtd_by_info(info);
>        struct nand_chip *chip = mtd->priv;
>
>        /* use the common timing to make a try */
> -       pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
> +       if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
> +               return 0;
>        chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
> -       if (info->is_ready)
> +       if (nand->is_ready)
>                return 1;
>        else
>                return 0;
> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>  static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  {
>        struct pxa3xx_nand_info *info = mtd->priv;
> -       struct platform_device *pdev = info->pdev;
> +       struct pxa3xx_nand *nand = info->nand_data;
> +       struct platform_device *pdev = nand->pdev;
>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>        struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>        const struct pxa3xx_nand_flash *f = NULL;
> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>        uint64_t chipsize;
>        int i, ret, num;
>
> +       nand->chip_select = info->chip_select;
>        if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>                goto KEEP_CONFIG;
>
>        ret = pxa3xx_nand_sensing(info);
>        if (!ret) {
> -               kfree(mtd);
> -               info->mtd = NULL;
> -               printk(KERN_INFO "There is no nand chip on cs 0!\n");
> +               free_cs_resource(info, nand->chip_select);
> +               printk(KERN_INFO "There is no nand chip on cs %d!\n",
> +                               nand->chip_select);

dev_err()?

>
>                return -EINVAL;
>        }
>
>        chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
> -       id = *((uint16_t *)(info->data_buff));
> +       id = *((uint16_t *)(nand->data_buff));
>        if (id != 0)
>                printk(KERN_INFO "Detect a flash id %x\n", id);
>        else {
> -               kfree(mtd);
> -               info->mtd = NULL;
> -               printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
> -
> +               free_cs_resource(info, nand->chip_select);
>                return -EINVAL;
>        }
>
> @@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>        }
>
>        if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
> -               kfree(mtd);
> -               info->mtd = NULL;
> +               free_cs_resource(info, nand->chip_select);
>                printk(KERN_ERR "ERROR!! flash not defined!!!\n");

Also here.

>
>                return -EINVAL;
>        }
>
> -       pxa3xx_nand_config_flash(info, f);
> +       if (pxa3xx_nand_config_flash(info, f)) {
> +               printk(KERN_ERR "ERROR! Configure failed\n");

And here. Giving no hint about which driver or function causes this
message makes debugging harder than it should be.

[...]

>  #ifdef CONFIG_PM
> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>        struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>        struct mtd_info *mtd = info->mtd;
>
> -       nand_writel(info, NDTR0CS0, info->ndtr0cs0);
> -       nand_writel(info, NDTR1CS0, info->ndtr1cs0);
> +       nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
> +       nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>        clk_enable(info->clk);

This won't compile.



Daniel

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-22 11:39   ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-06-22 11:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jun 22, 2011 at 5:17 AM, Lei Wen <leiwen@marvell.com> wrote:
> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
>
> Update platform driver to support this feature.
>
> Another 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.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
> ?arch/arm/mach-mmp/aspenite.c ? ? ? ? ? ? ? ? | ? ?5 +-
> ?arch/arm/mach-pxa/cm-x300.c ? ? ? ? ? ? ? ? ?| ? ?5 +-
> ?arch/arm/mach-pxa/colibri-pxa3xx.c ? ? ? ? ? | ? ?5 +-
> ?arch/arm/mach-pxa/littleton.c ? ? ? ? ? ? ? ?| ? ?5 +-
> ?arch/arm/mach-pxa/mxm8x10.c ? ? ? ? ? ? ? ? ?| ? ?9 +-
> ?arch/arm/mach-pxa/raumfeld.c ? ? ? ? ? ? ? ? | ? ?5 +-
> ?arch/arm/mach-pxa/zylonite.c ? ? ? ? ? ? ? ? | ? ?5 +-
> ?arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | ? ?8 +-
> ?drivers/mtd/nand/pxa3xx_nand.c ? ? ? ? ? ? ? | ?735 +++++++++++++++-----------
> ?9 files changed, 444 insertions(+), 338 deletions(-)

This patch doesn't apply for me on top of Linus' master branch. Which
tree are you based on currently?

[...]

> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index 30689cc..259b8d5 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -92,11 +92,13 @@
> ?#define NDCB0_ADDR_CYC_SHIFT ? (16)
>
> ?/* macros for registers read/write */
> -#define nand_writel(info, off, val) ? ?\
> - ? ? ? __raw_writel((val), (info)->mmio_base + (off))
> +#define nand_writel(nand, off, val) ? ?\
> + ? ? ? __raw_writel((val), (nand)->mmio_base + (off))
>
> -#define nand_readl(info, off) ? ? ? ? ?\
> - ? ? ? __raw_readl((info)->mmio_base + (off))
> +#define nand_readl(nand, off) ? ? ? ? ?\
> + ? ? ? __raw_readl((nand)->mmio_base + (off))
> +#define get_mtd_by_info(info) ? ? ? ? ?\
> + ? ? ? (struct mtd_info *)((void *)info - sizeof(struct mtd_info))
>
> ?/* error code and state */
> ?enum {
> @@ -110,6 +112,7 @@ enum {
>
> ?enum {
> ? ? ? ?STATE_IDLE = 0,
> + ? ? ? STATE_PREPARED,
> ? ? ? ?STATE_CMD_HANDLE,
> ? ? ? ?STATE_DMA_READING,
> ? ? ? ?STATE_DMA_WRITING,
> @@ -123,63 +126,63 @@ enum {
> ?struct pxa3xx_nand_info {
> ? ? ? ?struct nand_chip ? ? ? ?nand_chip;
>
> - ? ? ? struct nand_hw_control ?controller;
> - ? ? ? struct platform_device ? *pdev;
> ? ? ? ?struct pxa3xx_nand_cmdset *cmdset;
> + ? ? ? /* page size of attached chip */
> + ? ? ? uint16_t ? ? ? ? ? ? ? ?page_size;
> + ? ? ? uint8_t ? ? ? ? ? ? ? ? chip_select;
> + ? ? ? uint8_t ? ? ? ? ? ? ? ? use_ecc;
> +
> + ? ? ? /* calculated from pxa3xx_nand_flash data */
> + ? ? ? uint8_t ? ? ? ? ? ? ? ? col_addr_cycles;
> + ? ? ? uint8_t ? ? ? ? ? ? ? ? row_addr_cycles;
> + ? ? ? uint8_t ? ? ? ? ? ? ? ? read_id_bytes;
> +
> + ? ? ? /* cached register value */
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?reg_ndcr;
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr0cs0;
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr1cs0;
>
> + ? ? ? void ? ? ? ? ? ? ? ? ? ?*nand_data;
> +};
> +
> +struct pxa3xx_nand {
> ? ? ? ?struct clk ? ? ? ? ? ? ?*clk;
> ? ? ? ?void __iomem ? ? ? ? ? ?*mmio_base;
> ? ? ? ?unsigned long ? ? ? ? ? mmio_phys;
> + ? ? ? struct nand_hw_control ?controller;
> + ? ? ? struct completion ? ? ? cmd_complete;
> + ? ? ? struct platform_device ? *pdev;
>
> - ? ? ? unsigned int ? ? ? ? ? ?buf_start;
> - ? ? ? unsigned int ? ? ? ? ? ?buf_count;
> -
> - ? ? ? struct mtd_info ? ? ? ? *mtd;
> ? ? ? ?/* DMA information */
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? drcmr_dat;
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? drcmr_cmd;
> -
> - ? ? ? unsigned char ? ? ? ? ? *data_buff;
> - ? ? ? unsigned char ? ? ? ? ? *oob_buff;
> - ? ? ? dma_addr_t ? ? ? ? ? ? ?data_buff_phys;
> - ? ? ? size_t ? ? ? ? ? ? ? ? ?data_buff_size;
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? data_dma_ch;
> - ? ? ? struct pxa_dma_desc ? ? *data_desc;
> + ? ? ? dma_addr_t ? ? ? ? ? ? ?data_buff_phys;
> ? ? ? ?dma_addr_t ? ? ? ? ? ? ?data_desc_addr;
> + ? ? ? struct pxa_dma_desc ? ? *data_desc;
>
> - ? ? ? uint32_t ? ? ? ? ? ? ? ?reg_ndcr;
> -
> - ? ? ? /* saved column/page_addr during CMD_SEQIN */
> - ? ? ? int ? ? ? ? ? ? ? ? ? ? seqin_column;
> - ? ? ? int ? ? ? ? ? ? ? ? ? ? seqin_page_addr;
> + ? ? ? struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?command;
> + ? ? ? uint16_t ? ? ? ? ? ? ? ?data_size; ? ? ?/* data size in FIFO */
> + ? ? ? uint16_t ? ? ? ? ? ? ? ?oob_size;
> + ? ? ? unsigned char ? ? ? ? ? *data_buff;
> + ? ? ? unsigned char ? ? ? ? ? *oob_buff;
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?buf_start;
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?buf_count;
>
> ? ? ? ?/* relate to the command */
> ? ? ? ?unsigned int ? ? ? ? ? ?state;
> -
> + ? ? ? uint8_t ? ? ? ? ? ? ? ? chip_select;
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? use_ecc; ? ? ? ?/* use HW ECC ? */
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? use_dma; ? ? ? ?/* use DMA ? */
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? is_ready;
> -
> - ? ? ? unsigned int ? ? ? ? ? ?page_size; ? ? ?/* page size of attached chip */
> - ? ? ? unsigned int ? ? ? ? ? ?data_size; ? ? ?/* data size in FIFO */
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? retcode;
> - ? ? ? struct completion ? ? ? cmd_complete;
>
> ? ? ? ?/* generated NDCBx register values */
> + ? ? ? uint8_t ? ? ? ? ? ? ? ? total_cmds;
> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb0;
> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb1;
> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb2;
> -
> - ? ? ? /* timing calcuted from setting */
> - ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr0cs0;
> - ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr1cs0;
> -
> - ? ? ? /* calculated from pxa3xx_nand_flash data */
> - ? ? ? size_t ? ? ? ? ?oob_size;
> - ? ? ? size_t ? ? ? ? ?read_id_bytes;
> -
> - ? ? ? unsigned int ? ?col_addr_cycles;
> - ? ? ? unsigned int ? ?row_addr_cycles;
> ?};
>
> ?static int use_dma = 1;
> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
> ?/* Define a default flash type setting serve as flash detecting only */
> ?#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>
> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>
> ?#define NDTR0_tCH(c) ? (min((c), 7) << 19)
> ?#define NDTR0_tCS(c) ? (min((c), 7) << 16)
> @@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
> ?static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const struct pxa3xx_nand_timing *t)
> ?{
> - ? ? ? unsigned long nand_clk = clk_get_rate(info->clk);
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> + ? ? ? unsigned long nand_clk;
> ? ? ? ?uint32_t ndtr0, ndtr1;
>
> + ? ? ? nand_clk = clk_get_rate(nand->clk);
> ? ? ? ?ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
> ? ? ? ? ? ? ? ?NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
> ? ? ? ? ? ? ? ?NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
> @@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>
> ? ? ? ?info->ndtr0cs0 = ndtr0;
> ? ? ? ?info->ndtr1cs0 = ndtr1;
> - ? ? ? nand_writel(info, NDTR0CS0, ndtr0);
> - ? ? ? nand_writel(info, NDTR1CS0, ndtr1);
> + ? ? ? nand_writel(nand, NDTR0CS0, ndtr0);
> + ? ? ? nand_writel(nand, NDTR1CS0, ndtr1);
> ?}
>
> ?static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
> ?{
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> ? ? ? ?int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
>
> - ? ? ? info->data_size = info->page_size;
> + ? ? ? nand->data_size = info->page_size;
> ? ? ? ?if (!oob_enable) {
> - ? ? ? ? ? ? ? info->oob_size = 0;
> + ? ? ? ? ? ? ? nand->oob_size = 0;
> ? ? ? ? ? ? ? ?return;
> ? ? ? ?}
>
> ? ? ? ?switch (info->page_size) {
> ? ? ? ?case 2048:
> - ? ? ? ? ? ? ? info->oob_size = (info->use_ecc) ? 40 : 64;
> + ? ? ? ? ? ? ? nand->oob_size = (info->use_ecc) ? 40 : 64;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?case 512:
> - ? ? ? ? ? ? ? info->oob_size = (info->use_ecc) ? 8 : 16;
> + ? ? ? ? ? ? ? nand->oob_size = (info->use_ecc) ? 8 : 16;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?}
> ?}
> @@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
> ?* 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_info *info)
> +static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
> ?{
> + ? ? ? struct pxa3xx_nand_info *info;
> ? ? ? ?uint32_t ndcr;
>
> + ? ? ? info = nand->info[nand->chip_select];
> ? ? ? ?ndcr = info->reg_ndcr;
> - ? ? ? ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
> - ? ? ? ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
> + ? ? ? ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
> + ? ? ? ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
> ? ? ? ?ndcr |= NDCR_ND_RUN;
>
> ? ? ? ?/* clear status bits and run */
> - ? ? ? nand_writel(info, NDCR, 0);
> - ? ? ? nand_writel(info, NDSR, NDSR_MASK);
> - ? ? ? nand_writel(info, NDCR, ndcr);
> + ? ? ? nand_writel(nand, NDCR, 0);
> + ? ? ? nand_writel(nand, NDSR, NDSR_MASK);
> + ? ? ? nand_writel(nand, NDCR, ndcr);
> ?}
>
> -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
> +static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
> ?{
> ? ? ? ?uint32_t ndcr;
> ? ? ? ?int timeout = NAND_STOP_DELAY;
>
> ? ? ? ?/* wait RUN bit in NDCR become 0 */
> - ? ? ? ndcr = nand_readl(info, NDCR);
> + ? ? ? ndcr = nand_readl(nand, NDCR);
> ? ? ? ?while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
> - ? ? ? ? ? ? ? ndcr = nand_readl(info, NDCR);
> + ? ? ? ? ? ? ? ndcr = nand_readl(nand, NDCR);
> ? ? ? ? ? ? ? ?udelay(1);
> ? ? ? ?}
>
> ? ? ? ?if (timeout <= 0) {
> ? ? ? ? ? ? ? ?ndcr &= ~NDCR_ND_RUN;
> - ? ? ? ? ? ? ? nand_writel(info, NDCR, ndcr);
> + ? ? ? ? ? ? ? nand_writel(nand, NDCR, ndcr);
> ? ? ? ?}
> ? ? ? ?/* clear status bits */
> - ? ? ? nand_writel(info, NDSR, NDSR_MASK);
> + ? ? ? nand_writel(nand, NDSR, NDSR_MASK);
> ?}
>
> -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> +static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
> ?{
> ? ? ? ?uint32_t ndcr;
>
> - ? ? ? ndcr = nand_readl(info, NDCR);
> - ? ? ? nand_writel(info, NDCR, ndcr & ~int_mask);
> + ? ? ? ndcr = nand_readl(nand, NDCR);
> + ? ? ? nand_writel(nand, NDCR, ndcr & ~int_mask);
> ?}
>
> -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
> ?{
> ? ? ? ?uint32_t ndcr;
>
> - ? ? ? ndcr = nand_readl(info, NDCR);
> - ? ? ? nand_writel(info, NDCR, ndcr | int_mask);
> + ? ? ? ndcr = nand_readl(nand, NDCR);
> + ? ? ? nand_writel(nand, NDCR, ndcr | int_mask);
> ?}
>
> -static void handle_data_pio(struct pxa3xx_nand_info *info)
> +static void handle_data_pio(struct pxa3xx_nand *nand)
> ?{
> - ? ? ? switch (info->state) {
> + ? ? ? switch (nand->state) {
> ? ? ? ?case STATE_PIO_WRITING:
> - ? ? ? ? ? ? ? __raw_writesl(info->mmio_base + NDDB, info->data_buff,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->data_size, 4));
> - ? ? ? ? ? ? ? if (info->oob_size > 0)
> - ? ? ? ? ? ? ? ? ? ? ? __raw_writesl(info->mmio_base + NDDB, info->oob_buff,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->oob_size, 4));
> + ? ? ? ? ? ? ? __raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->data_size, 4));
> + ? ? ? ? ? ? ? if (nand->oob_size > 0)
> + ? ? ? ? ? ? ? ? ? ? ? __raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->oob_size, 4));
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?case STATE_PIO_READING:
> - ? ? ? ? ? ? ? __raw_readsl(info->mmio_base + NDDB, info->data_buff,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->data_size, 4));
> - ? ? ? ? ? ? ? if (info->oob_size > 0)
> - ? ? ? ? ? ? ? ? ? ? ? __raw_readsl(info->mmio_base + NDDB, info->oob_buff,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->oob_size, 4));
> + ? ? ? ? ? ? ? __raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->data_size, 4));
> + ? ? ? ? ? ? ? if (nand->oob_size > 0)
> + ? ? ? ? ? ? ? ? ? ? ? __raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->oob_size, 4));
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?default:
> ? ? ? ? ? ? ? ?printk(KERN_ERR "%s: invalid state %d\n", __func__,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->state);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->state);

Can't you use dev_err() here?

> ? ? ? ? ? ? ? ?BUG();

Is crashing the entire kernel really necessary here?

> ? ? ? ?}
> ?}
>
> -static void start_data_dma(struct pxa3xx_nand_info *info)
> +static void start_data_dma(struct pxa3xx_nand *nand)
> ?{
> - ? ? ? struct pxa_dma_desc *desc = info->data_desc;
> - ? ? ? int dma_len = ALIGN(info->data_size + info->oob_size, 32);
> + ? ? ? struct pxa_dma_desc *desc = nand->data_desc;
> + ? ? ? int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
>
> ? ? ? ?desc->ddadr = DDADR_STOP;
> ? ? ? ?desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
>
> - ? ? ? switch (info->state) {
> + ? ? ? switch (nand->state) {
> ? ? ? ?case STATE_DMA_WRITING:
> - ? ? ? ? ? ? ? desc->dsadr = info->data_buff_phys;
> - ? ? ? ? ? ? ? desc->dtadr = info->mmio_phys + NDDB;
> + ? ? ? ? ? ? ? desc->dsadr = nand->data_buff_phys;
> + ? ? ? ? ? ? ? desc->dtadr = nand->mmio_phys + NDDB;
> ? ? ? ? ? ? ? ?desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?case STATE_DMA_READING:
> - ? ? ? ? ? ? ? desc->dtadr = info->data_buff_phys;
> - ? ? ? ? ? ? ? desc->dsadr = info->mmio_phys + NDDB;
> + ? ? ? ? ? ? ? desc->dtadr = nand->data_buff_phys;
> + ? ? ? ? ? ? ? desc->dsadr = nand->mmio_phys + NDDB;
> ? ? ? ? ? ? ? ?desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?default:
> ? ? ? ? ? ? ? ?printk(KERN_ERR "%s: invalid state %d\n", __func__,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->state);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->state);
> ? ? ? ? ? ? ? ?BUG();
> ? ? ? ?}
>
> - ? ? ? DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
> - ? ? ? DDADR(info->data_dma_ch) = info->data_desc_addr;
> - ? ? ? DCSR(info->data_dma_ch) |= DCSR_RUN;
> + ? ? ? DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
> + ? ? ? DDADR(nand->data_dma_ch) = nand->data_desc_addr;
> + ? ? ? DCSR(nand->data_dma_ch) |= DCSR_RUN;
> ?}
>
> ?static void pxa3xx_nand_data_dma_irq(int channel, void *data)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = data;
> + ? ? ? struct pxa3xx_nand *nand = data;
> ? ? ? ?uint32_t dcsr;
>
> ? ? ? ?dcsr = DCSR(channel);
> ? ? ? ?DCSR(channel) = dcsr;
>
> ? ? ? ?if (dcsr & DCSR_BUSERR) {
> - ? ? ? ? ? ? ? info->retcode = ERR_DMABUSERR;
> + ? ? ? ? ? ? ? nand->retcode = ERR_DMABUSERR;
> ? ? ? ?}
>
> - ? ? ? info->state = STATE_DMA_DONE;
> - ? ? ? enable_int(info, NDCR_INT_MASK);
> - ? ? ? nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
> + ? ? ? nand->state = STATE_DMA_DONE;
> + ? ? ? enable_int(nand, NDCR_INT_MASK);
> + ? ? ? nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
> ?}
>
> ?static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = devid;
> - ? ? ? unsigned int status, is_completed = 0;
> + ? ? ? struct pxa3xx_nand *nand = devid;
> + ? ? ? struct pxa3xx_nand_info *info;
> + ? ? ? unsigned int status, is_completed = 0, cs;
> + ? ? ? unsigned int ready, cmd_done, page_done, badblock_detect;
>
> - ? ? ? status = nand_readl(info, NDSR);
> + ? ? ? cs ? ? ? ? ? ? ?= nand->chip_select;
> + ? ? ? ready ? ? ? ? ? = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
> + ? ? ? cmd_done ? ? ? ?= (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
> + ? ? ? page_done ? ? ? = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
> + ? ? ? badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
> + ? ? ? info ? ? ? ? ? ?= nand->info[cs];
>
> + ? ? ? status = nand_readl(nand, NDSR);
> ? ? ? ?if (status & NDSR_DBERR)
> - ? ? ? ? ? ? ? info->retcode = ERR_DBERR;
> + ? ? ? ? ? ? ? nand->retcode = ERR_DBERR;
> ? ? ? ?if (status & NDSR_SBERR)
> - ? ? ? ? ? ? ? info->retcode = ERR_SBERR;
> + ? ? ? ? ? ? ? nand->retcode = ERR_SBERR;
> ? ? ? ?if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
> ? ? ? ? ? ? ? ?/* whether use dma to transfer data */
> - ? ? ? ? ? ? ? if (info->use_dma) {
> - ? ? ? ? ? ? ? ? ? ? ? disable_int(info, NDCR_INT_MASK);
> - ? ? ? ? ? ? ? ? ? ? ? info->state = (status & NDSR_RDDREQ) ?
> + ? ? ? ? ? ? ? if (nand->use_dma) {
> + ? ? ? ? ? ? ? ? ? ? ? disable_int(nand, NDCR_INT_MASK);
> + ? ? ? ? ? ? ? ? ? ? ? nand->state = (status & NDSR_RDDREQ) ?
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?STATE_DMA_READING : STATE_DMA_WRITING;
> - ? ? ? ? ? ? ? ? ? ? ? start_data_dma(info);
> + ? ? ? ? ? ? ? ? ? ? ? start_data_dma(nand);
> ? ? ? ? ? ? ? ? ? ? ? ?goto NORMAL_IRQ_EXIT;
> ? ? ? ? ? ? ? ?} else {
> - ? ? ? ? ? ? ? ? ? ? ? info->state = (status & NDSR_RDDREQ) ?
> + ? ? ? ? ? ? ? ? ? ? ? nand->state = (status & NDSR_RDDREQ) ?
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?STATE_PIO_READING : STATE_PIO_WRITING;
> - ? ? ? ? ? ? ? ? ? ? ? handle_data_pio(info);
> + ? ? ? ? ? ? ? ? ? ? ? handle_data_pio(nand);
> ? ? ? ? ? ? ? ?}
> ? ? ? ?}
> - ? ? ? if (status & NDSR_CS0_CMDD) {
> - ? ? ? ? ? ? ? info->state = STATE_CMD_DONE;
> + ? ? ? if (status & cmd_done) {
> + ? ? ? ? ? ? ? nand->state = STATE_CMD_DONE;
> ? ? ? ? ? ? ? ?is_completed = 1;
> ? ? ? ?}
> - ? ? ? if (status & NDSR_FLASH_RDY) {
> - ? ? ? ? ? ? ? info->is_ready = 1;
> - ? ? ? ? ? ? ? info->state = STATE_READY;
> + ? ? ? if (status & ready) {
> + ? ? ? ? ? ? ? nand->is_ready = 1;
> + ? ? ? ? ? ? ? nand->state = STATE_READY;
> ? ? ? ?}
>
> ? ? ? ?if (status & NDSR_WRCMDREQ) {
> - ? ? ? ? ? ? ? nand_writel(info, NDSR, NDSR_WRCMDREQ);
> + ? ? ? ? ? ? ? nand_writel(nand, NDSR, NDSR_WRCMDREQ);
> ? ? ? ? ? ? ? ?status &= ~NDSR_WRCMDREQ;
> - ? ? ? ? ? ? ? info->state = STATE_CMD_HANDLE;
> - ? ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb0);
> - ? ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb1);
> - ? ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb2);
> + ? ? ? ? ? ? ? nand->state = STATE_CMD_HANDLE;
> + ? ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb0);
> + ? ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb1);
> + ? ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb2);
> ? ? ? ?}
>
> ? ? ? ?/* clear NDSR to let the controller exit the IRQ */
> - ? ? ? nand_writel(info, NDSR, status);
> + ? ? ? nand_writel(nand, NDSR, status);
> ? ? ? ?if (is_completed)
> - ? ? ? ? ? ? ? complete(&info->cmd_complete);
> + ? ? ? ? ? ? ? complete(&nand->cmd_complete);
> ?NORMAL_IRQ_EXIT:
> ? ? ? ?return IRQ_HANDLED;
> ?}
>
> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
> -{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> - ? ? ? return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
> -}
> -
> ?static inline int is_buf_blank(uint8_t *buf, size_t len)
> ?{
> ? ? ? ?for (; len > 0; len--)
> @@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
> ? ? ? ?return 1;
> ?}
>
> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
> ? ? ? ? ? ? ? ?uint16_t column, int page_addr)
> ?{
> ? ? ? ?uint16_t cmd;
> ? ? ? ?int addr_cycle, exec_cmd, ndcb0;
> - ? ? ? struct mtd_info *mtd = info->mtd;
> + ? ? ? struct mtd_info *mtd;
> + ? ? ? struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>
> - ? ? ? ndcb0 = 0;
> + ? ? ? mtd = get_mtd_by_info(info);
> + ? ? ? ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
> ? ? ? ?addr_cycle = 0;
> ? ? ? ?exec_cmd = 1;
>
> ? ? ? ?/* reset data and oob column point to handle data */
> - ? ? ? info->buf_start ? ? ? ? = 0;
> - ? ? ? info->buf_count ? ? ? ? = 0;
> - ? ? ? info->oob_size ? ? ? ? ?= 0;
> - ? ? ? info->use_ecc ? ? ? ? ? = 0;
> - ? ? ? info->is_ready ? ? ? ? ?= 0;
> - ? ? ? info->retcode ? ? ? ? ? = ERR_NONE;
> + ? ? ? nand->buf_start ? ? ? ? = 0;
> + ? ? ? nand->buf_count ? ? ? ? = 0;
> + ? ? ? nand->oob_size ? ? ? ? ?= 0;
> + ? ? ? nand->use_ecc ? ? ? ? ? = 0;
> + ? ? ? nand->is_ready ? ? ? ? ?= 0;
> + ? ? ? nand->retcode ? ? ? ? ? = ERR_NONE;
> + ? ? ? nand->data_size ? ? ? ? = 0;
> + ? ? ? nand->use_dma ? ? ? ? ? = 0;
> + ? ? ? nand->command ? ? ? ? ? = command;
>
> ? ? ? ?switch (command) {
> ? ? ? ?case NAND_CMD_READ0:
> ? ? ? ?case NAND_CMD_PAGEPROG:
> - ? ? ? ? ? ? ? info->use_ecc = 1;
> + ? ? ? ? ? ? ? nand->use_ecc = 1;
> ? ? ? ?case NAND_CMD_READOOB:
> ? ? ? ? ? ? ? ?pxa3xx_set_datasize(info);
> + ? ? ? ? ? ? ? nand->oob_buff = nand->data_buff + nand->data_size;
> + ? ? ? ? ? ? ? nand->use_dma = use_dma;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?case NAND_CMD_SEQIN:
> ? ? ? ? ? ? ? ?exec_cmd = 0;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?default:
> - ? ? ? ? ? ? ? info->ndcb1 = 0;
> - ? ? ? ? ? ? ? info->ndcb2 = 0;
> + ? ? ? ? ? ? ? nand->ndcb1 = 0;
> + ? ? ? ? ? ? ? nand->ndcb2 = 0;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?}
>
> - ? ? ? info->ndcb0 = ndcb0;
> + ? ? ? nand->ndcb0 = ndcb0;
> ? ? ? ?addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?+ info->col_addr_cycles);
>
> @@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ?case NAND_CMD_READ0:
> ? ? ? ? ? ? ? ?cmd = info->cmdset->read1;
> ? ? ? ? ? ? ? ?if (command == NAND_CMD_READOOB)
> - ? ? ? ? ? ? ? ? ? ? ? info->buf_start = mtd->writesize + column;
> + ? ? ? ? ? ? ? ? ? ? ? nand->buf_start = mtd->writesize + column;
> ? ? ? ? ? ? ? ?else
> - ? ? ? ? ? ? ? ? ? ? ? info->buf_start = column;
> + ? ? ? ? ? ? ? ? ? ? ? nand->buf_start = column;
>
> ? ? ? ? ? ? ? ?if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0)
> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| addr_cycle
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| (cmd & NDCB0_CMD1_MASK);
> ? ? ? ? ? ? ? ?else
> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0)
> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_DBC
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| addr_cycle
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
> @@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ?case NAND_CMD_SEQIN:
> ? ? ? ? ? ? ? ?/* small page addr setting */
> ? ? ? ? ? ? ? ?if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| (column & 0xFF);
>
> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb2 = 0;
> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = 0;
> ? ? ? ? ? ? ? ?} else {
> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb1 = ((page_addr & 0xFFFF) << 16)
> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| (column & 0xFFFF);
>
> ? ? ? ? ? ? ? ? ? ? ? ?if (page_addr & 0xFF0000)
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->ndcb2 = (page_addr & 0xFF0000) >> 16;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
> ? ? ? ? ? ? ? ? ? ? ? ?else
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->ndcb2 = 0;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = 0;
> ? ? ? ? ? ? ? ?}
>
> - ? ? ? ? ? ? ? info->buf_count = mtd->writesize + mtd->oobsize;
> - ? ? ? ? ? ? ? memset(info->data_buff, 0xFF, info->buf_count);
> + ? ? ? ? ? ? ? nand->buf_count = mtd->writesize + mtd->oobsize;
> + ? ? ? ? ? ? ? memset(nand->data_buff, 0xFF, nand->buf_count);
>
> ? ? ? ? ? ? ? ?break;
>
> ? ? ? ?case NAND_CMD_PAGEPROG:
> - ? ? ? ? ? ? ? if (is_buf_blank(info->data_buff,
> + ? ? ? ? ? ? ? if (is_buf_blank(nand->data_buff,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(mtd->writesize + mtd->oobsize))) {
> ? ? ? ? ? ? ? ? ? ? ? ?exec_cmd = 0;
> ? ? ? ? ? ? ? ? ? ? ? ?break;
> ? ? ? ? ? ? ? ?}
>
> ? ? ? ? ? ? ? ?cmd = info->cmdset->program;
> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_AUTO_RS
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ST_ROW_EN
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_DBC
> @@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>
> ? ? ? ?case NAND_CMD_READID:
> ? ? ? ? ? ? ? ?cmd = info->cmdset->read_id;
> - ? ? ? ? ? ? ? info->buf_count = info->read_id_bytes;
> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(3)
> + ? ? ? ? ? ? ? nand->buf_count = info->read_id_bytes;
> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(3)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(1)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>
> - ? ? ? ? ? ? ? info->data_size = 8;
> + ? ? ? ? ? ? ? nand->data_size = 8;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?case NAND_CMD_STATUS:
> ? ? ? ? ? ? ? ?cmd = info->cmdset->read_status;
> - ? ? ? ? ? ? ? info->buf_count = 1;
> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(4)
> + ? ? ? ? ? ? ? nand->buf_count = 1;
> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(4)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(1)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>
> - ? ? ? ? ? ? ? info->data_size = 8;
> + ? ? ? ? ? ? ? nand->data_size = 8;
> ? ? ? ? ? ? ? ?break;
>
> ? ? ? ?case NAND_CMD_ERASE1:
> ? ? ? ? ? ? ? ?cmd = info->cmdset->erase;
> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(2)
> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(2)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_AUTO_RS
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(3)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_DBC
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
> - ? ? ? ? ? ? ? info->ndcb1 = page_addr;
> - ? ? ? ? ? ? ? info->ndcb2 = 0;
> + ? ? ? ? ? ? ? nand->ndcb1 = page_addr;
> + ? ? ? ? ? ? ? nand->ndcb2 = 0;
>
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?case NAND_CMD_RESET:
> ? ? ? ? ? ? ? ?cmd = info->cmdset->reset;
> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(5)
> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(5)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>
> ? ? ? ? ? ? ? ?break;
> @@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int column, int page_addr)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> ? ? ? ?int ret, exec_cmd;
>
> ? ? ? ?/*
> @@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
> ? ? ? ?if (info->reg_ndcr & NDCR_DWIDTH_M)
> ? ? ? ? ? ? ? ?column /= 2;
>
> - ? ? ? exec_cmd = prepare_command_pool(info, command, column, page_addr);
> + ? ? ? /*
> + ? ? ? ?* There may be different NAND chip hooked to
> + ? ? ? ?* different chip select, so check whether
> + ? ? ? ?* chip select has been changed, if yes, reset the timing
> + ? ? ? ?*/
> + ? ? ? if (nand->chip_select != info->chip_select) {
> + ? ? ? ? ? ? ? nand->chip_select = info->chip_select;
> + ? ? ? ? ? ? ? nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
> + ? ? ? ? ? ? ? nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
> + ? ? ? }
> +
> + ? ? ? nand->state = STATE_PREPARED;
> + ? ? ? exec_cmd = prepare_command_pool(nand, command, column, page_addr);
> ? ? ? ?if (exec_cmd) {
> - ? ? ? ? ? ? ? init_completion(&info->cmd_complete);
> - ? ? ? ? ? ? ? pxa3xx_nand_start(info);
> + ? ? ? ? ? ? ? init_completion(&nand->cmd_complete);
> + ? ? ? ? ? ? ? pxa3xx_nand_start(nand);
>
> - ? ? ? ? ? ? ? ret = wait_for_completion_timeout(&info->cmd_complete,
> + ? ? ? ? ? ? ? ret = wait_for_completion_timeout(&nand->cmd_complete,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CHIP_DELAY_TIMEOUT);
> ? ? ? ? ? ? ? ?if (!ret) {
> ? ? ? ? ? ? ? ? ? ? ? ?printk(KERN_ERR "Wait time out!!!\n");
> ? ? ? ? ? ? ? ? ? ? ? ?/* Stop State Machine for next command cycle */
> - ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_stop(info);
> + ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_stop(nand);
> ? ? ? ? ? ? ? ?}
> - ? ? ? ? ? ? ? info->state = STATE_IDLE;
> ? ? ? ?}
> + ? ? ? nand->state = STATE_IDLE;
> ?}
>
> ?static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
> @@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
> ? ? ? ? ? ? ? ?struct nand_chip *chip, uint8_t *buf, int page)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>
> ? ? ? ?chip->read_buf(mtd, buf, mtd->writesize);
> ? ? ? ?chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>
> - ? ? ? if (info->retcode == ERR_SBERR) {
> + ? ? ? if (nand->retcode == ERR_SBERR) {
> ? ? ? ? ? ? ? ?switch (info->use_ecc) {
> ? ? ? ? ? ? ? ?case 1:
> ? ? ? ? ? ? ? ? ? ? ? ?mtd->ecc_stats.corrected++;
> @@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
> ? ? ? ? ? ? ? ?default:
> ? ? ? ? ? ? ? ? ? ? ? ?break;
> ? ? ? ? ? ? ? ?}
> - ? ? ? } else if (info->retcode == ERR_DBERR) {
> + ? ? ? } else if (nand->retcode == ERR_DBERR) {
> ? ? ? ? ? ? ? ?/*
> ? ? ? ? ? ? ? ? * for blank page (all 0xff), HW will calculate its ECC as
> ? ? ? ? ? ? ? ? * 0, which is different from the ECC information within
> ? ? ? ? ? ? ? ? * OOB, ignore such double bit errors
> ? ? ? ? ? ? ? ? */
> ? ? ? ? ? ? ? ?if (is_buf_blank(buf, mtd->writesize))
> - ? ? ? ? ? ? ? ? ? ? ? info->retcode = ERR_NONE;
> + ? ? ? ? ? ? ? ? ? ? ? nand->retcode = ERR_NONE;
> ? ? ? ? ? ? ? ?else
> ? ? ? ? ? ? ? ? ? ? ? ?mtd->ecc_stats.failed++;
> ? ? ? ?}
> @@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
> ?static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> ? ? ? ?char retval = 0xFF;
>
> - ? ? ? if (info->buf_start < info->buf_count)
> + ? ? ? if (nand->buf_start < nand->buf_count)
> ? ? ? ? ? ? ? ?/* Has just send a new command? */
> - ? ? ? ? ? ? ? retval = info->data_buff[info->buf_start++];
> + ? ? ? ? ? ? ? retval = nand->data_buff[nand->buf_start++];
>
> ? ? ? ?return retval;
> ?}
> @@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
> ?static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> ? ? ? ?u16 retval = 0xFFFF;
>
> - ? ? ? if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
> - ? ? ? ? ? ? ? retval = *((u16 *)(info->data_buff+info->buf_start));
> - ? ? ? ? ? ? ? info->buf_start += 2;
> + ? ? ? if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
> + ? ? ? ? ? ? ? retval = *((u16 *)(nand->data_buff+nand->buf_start));
> + ? ? ? ? ? ? ? nand->buf_start += 2;
> ? ? ? ?}
> ? ? ? ?return retval;
> ?}
> @@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
> ?static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
> - ? ? ? int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> + ? ? ? int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>
> - ? ? ? memcpy(buf, info->data_buff + info->buf_start, real_len);
> - ? ? ? info->buf_start += real_len;
> + ? ? ? memcpy(buf, nand->data_buff + nand->buf_start, real_len);
> + ? ? ? nand->buf_start += real_len;
> ?}
>
> ?static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
> ? ? ? ? ? ? ? ?const uint8_t *buf, int len)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
> - ? ? ? int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> + ? ? ? int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>
> - ? ? ? memcpy(info->data_buff + info->buf_start, buf, real_len);
> - ? ? ? info->buf_start += real_len;
> + ? ? ? memcpy(nand->data_buff + nand->buf_start, buf, real_len);
> + ? ? ? nand->buf_start += real_len;
> ?}
>
> ?static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
> @@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
> ?static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>
> ? ? ? ?/* pxa3xx_nand_send_command has waited for command complete */
> ? ? ? ?if (this->state == FL_WRITING || this->state == FL_ERASING) {
> - ? ? ? ? ? ? ? if (info->retcode == ERR_NONE)
> + ? ? ? ? ? ? ? if (nand->retcode == ERR_NONE)
> ? ? ? ? ? ? ? ? ? ? ? ?return 0;
> ? ? ? ? ? ? ? ?else {
> ? ? ? ? ? ? ? ? ? ? ? ?/*
> @@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
> ?static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?const struct pxa3xx_nand_flash *f)
> ?{
> - ? ? ? struct platform_device *pdev = info->pdev;
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> + ? ? ? struct platform_device *pdev = nand->pdev;
> ? ? ? ?struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
> ? ? ? ?uint32_t ndcr = 0x0; /* enable all interrupts */
>
> @@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
> ? ? ? ?ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>
> ? ? ? ?info->reg_ndcr = ndcr;
> + ? ? ? info->use_ecc = 1;
>
> ? ? ? ?pxa3xx_nand_set_timing(info, f->timing);
> ? ? ? ?return 0;
> @@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>
> ?static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
> ?{
> - ? ? ? uint32_t ndcr = nand_readl(info, NDCR);
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> + ? ? ? uint32_t ndcr = nand_readl(nand, NDCR);
> +
> + ? ? ? if (info->chip_select > 0) {
> + ? ? ? ? ? ? ? printk(KERN_ERR "We could not detect configure"
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " if more than one cs is supported!!\n");
> + ? ? ? ? ? ? ? BUG();
> + ? ? ? }
> ? ? ? ?info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
> ? ? ? ?/* set info fields needed to read id */
> ? ? ? ?info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
> ? ? ? ?info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> ? ? ? ?info->cmdset = &default_cmdset;
>
> - ? ? ? info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> - ? ? ? info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
> + ? ? ? info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
> + ? ? ? info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
>
> ? ? ? ?return 0;
> ?}
> @@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
> ?*/
> ?#define MAX_BUFF_SIZE ?PAGE_SIZE
>
> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
> ?{
> - ? ? ? struct platform_device *pdev = info->pdev;
> - ? ? ? int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
> -
> - ? ? ? if (use_dma == 0) {
> - ? ? ? ? ? ? ? info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> - ? ? ? ? ? ? ? if (info->data_buff == NULL)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOMEM;
> - ? ? ? ? ? ? ? return 0;
> - ? ? ? }
> -
> - ? ? ? info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &info->data_buff_phys, GFP_KERNEL);
> - ? ? ? if (info->data_buff == NULL) {
> - ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> - ? ? ? ? ? ? ? return -ENOMEM;
> - ? ? ? }
> -
> - ? ? ? info->data_buff_size = MAX_BUFF_SIZE;
> - ? ? ? info->data_desc = (void *)info->data_buff + data_desc_offset;
> - ? ? ? info->data_desc_addr = info->data_buff_phys + data_desc_offset;
> + ? ? ? struct pxa3xx_nand *nand;
> + ? ? ? struct mtd_info *mtd;
>
> - ? ? ? info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_data_dma_irq, info);
> - ? ? ? if (info->data_dma_ch < 0) {
> - ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to request data dma\n");
> - ? ? ? ? ? ? ? dma_free_coherent(&pdev->dev, info->data_buff_size,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->data_buff, info->data_buff_phys);
> - ? ? ? ? ? ? ? return info->data_dma_ch;
> - ? ? ? }
> + ? ? ? if (!info)
> + ? ? ? ? ? ? ? return;
>
> - ? ? ? return 0;
> + ? ? ? nand = info->nand_data;
> + ? ? ? mtd = get_mtd_by_info(info);
> + ? ? ? kfree(mtd);
> + ? ? ? nand->info[cs] = NULL;
> ?}
>
> ?static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
> ?{
> - ? ? ? struct mtd_info *mtd = info->mtd;
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> + ? ? ? struct mtd_info *mtd = get_mtd_by_info(info);
> ? ? ? ?struct nand_chip *chip = mtd->priv;
>
> ? ? ? ?/* use the common timing to make a try */
> - ? ? ? pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
> + ? ? ? if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
> + ? ? ? ? ? ? ? return 0;
> ? ? ? ?chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
> - ? ? ? if (info->is_ready)
> + ? ? ? if (nand->is_ready)
> ? ? ? ? ? ? ? ?return 1;
> ? ? ? ?else
> ? ? ? ? ? ? ? ?return 0;
> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
> ?static int pxa3xx_nand_scan(struct mtd_info *mtd)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
> - ? ? ? struct platform_device *pdev = info->pdev;
> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
> + ? ? ? struct platform_device *pdev = nand->pdev;
> ? ? ? ?struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
> ? ? ? ?struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
> ? ? ? ?const struct pxa3xx_nand_flash *f = NULL;
> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
> ? ? ? ?uint64_t chipsize;
> ? ? ? ?int i, ret, num;
>
> + ? ? ? nand->chip_select = info->chip_select;
> ? ? ? ?if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
> ? ? ? ? ? ? ? ?goto KEEP_CONFIG;
>
> ? ? ? ?ret = pxa3xx_nand_sensing(info);
> ? ? ? ?if (!ret) {
> - ? ? ? ? ? ? ? kfree(mtd);
> - ? ? ? ? ? ? ? info->mtd = NULL;
> - ? ? ? ? ? ? ? printk(KERN_INFO "There is no nand chip on cs 0!\n");
> + ? ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
> + ? ? ? ? ? ? ? printk(KERN_INFO "There is no nand chip on cs %d!\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->chip_select);

dev_err()?

>
> ? ? ? ? ? ? ? ?return -EINVAL;
> ? ? ? ?}
>
> ? ? ? ?chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
> - ? ? ? id = *((uint16_t *)(info->data_buff));
> + ? ? ? id = *((uint16_t *)(nand->data_buff));
> ? ? ? ?if (id != 0)
> ? ? ? ? ? ? ? ?printk(KERN_INFO "Detect a flash id %x\n", id);
> ? ? ? ?else {
> - ? ? ? ? ? ? ? kfree(mtd);
> - ? ? ? ? ? ? ? info->mtd = NULL;
> - ? ? ? ? ? ? ? printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
> -
> + ? ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
> ? ? ? ? ? ? ? ?return -EINVAL;
> ? ? ? ?}
>
> @@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
> ? ? ? ?}
>
> ? ? ? ?if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
> - ? ? ? ? ? ? ? kfree(mtd);
> - ? ? ? ? ? ? ? info->mtd = NULL;
> + ? ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
> ? ? ? ? ? ? ? ?printk(KERN_ERR "ERROR!! flash not defined!!!\n");

Also here.

>
> ? ? ? ? ? ? ? ?return -EINVAL;
> ? ? ? ?}
>
> - ? ? ? pxa3xx_nand_config_flash(info, f);
> + ? ? ? if (pxa3xx_nand_config_flash(info, f)) {
> + ? ? ? ? ? ? ? printk(KERN_ERR "ERROR! Configure failed\n");

And here. Giving no hint about which driver or function causes this
message makes debugging harder than it should be.

[...]

> ?#ifdef CONFIG_PM
> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
> ? ? ? ?struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> ? ? ? ?struct mtd_info *mtd = info->mtd;
>
> - ? ? ? nand_writel(info, NDTR0CS0, info->ndtr0cs0);
> - ? ? ? nand_writel(info, NDTR1CS0, info->ndtr1cs0);
> + ? ? ? nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
> + ? ? ? nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
> ? ? ? ?clk_enable(info->clk);

This won't compile.



Daniel

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-22 11:39   ` Daniel Mack
@ 2011-07-08 17:38     ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-22 12:21 UTC (permalink / raw)
  To: Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

Hi Daniel,

On Wed, Jun 22, 2011 at 7:39 PM, Daniel Mack <zonque@gmail.com> wrote:
> On Wed, Jun 22, 2011 at 5:17 AM, Lei Wen <leiwen@marvell.com> wrote:
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>>  arch/arm/mach-pxa/littleton.c                |    5 +-
>>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>>  9 files changed, 444 insertions(+), 338 deletions(-)
>
> This patch doesn't apply for me on top of Linus' master branch. Which
> tree are you based on currently?

Actually it is based on Linux 3.0-rc2 and add two patches pending to
l2-mtd-2.6.git already:

mtd: pxa3xx_nand: fix nand detection issue
mtd: pxa3xx_nand: Fix blank page ECC mismatch

>
> [...]
>
>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
>> index 30689cc..259b8d5 100644
>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>> +++ b/drivers/mtd/nand/pxa3xx_nand.c
>> @@ -92,11 +92,13 @@
>>  #define NDCB0_ADDR_CYC_SHIFT   (16)
>>
>>  /* macros for registers read/write */
>> -#define nand_writel(info, off, val)    \
>> -       __raw_writel((val), (info)->mmio_base + (off))
>> +#define nand_writel(nand, off, val)    \
>> +       __raw_writel((val), (nand)->mmio_base + (off))
>>
>> -#define nand_readl(info, off)          \
>> -       __raw_readl((info)->mmio_base + (off))
>> +#define nand_readl(nand, off)          \
>> +       __raw_readl((nand)->mmio_base + (off))
>> +#define get_mtd_by_info(info)          \
>> +       (struct mtd_info *)((void *)info - sizeof(struct mtd_info))
>>
>>  /* error code and state */
>>  enum {
>> @@ -110,6 +112,7 @@ enum {
>>
>>  enum {
>>        STATE_IDLE = 0,
>> +       STATE_PREPARED,
>>        STATE_CMD_HANDLE,
>>        STATE_DMA_READING,
>>        STATE_DMA_WRITING,
>> @@ -123,63 +126,63 @@ enum {
>>  struct pxa3xx_nand_info {
>>        struct nand_chip        nand_chip;
>>
>> -       struct nand_hw_control  controller;
>> -       struct platform_device   *pdev;
>>        struct pxa3xx_nand_cmdset *cmdset;
>> +       /* page size of attached chip */
>> +       uint16_t                page_size;
>> +       uint8_t                 chip_select;
>> +       uint8_t                 use_ecc;
>> +
>> +       /* calculated from pxa3xx_nand_flash data */
>> +       uint8_t                 col_addr_cycles;
>> +       uint8_t                 row_addr_cycles;
>> +       uint8_t                 read_id_bytes;
>> +
>> +       /* cached register value */
>> +       uint32_t                reg_ndcr;
>> +       uint32_t                ndtr0cs0;
>> +       uint32_t                ndtr1cs0;
>>
>> +       void                    *nand_data;
>> +};
>> +
>> +struct pxa3xx_nand {
>>        struct clk              *clk;
>>        void __iomem            *mmio_base;
>>        unsigned long           mmio_phys;
>> +       struct nand_hw_control  controller;
>> +       struct completion       cmd_complete;
>> +       struct platform_device   *pdev;
>>
>> -       unsigned int            buf_start;
>> -       unsigned int            buf_count;
>> -
>> -       struct mtd_info         *mtd;
>>        /* DMA information */
>>        int                     drcmr_dat;
>>        int                     drcmr_cmd;
>> -
>> -       unsigned char           *data_buff;
>> -       unsigned char           *oob_buff;
>> -       dma_addr_t              data_buff_phys;
>> -       size_t                  data_buff_size;
>>        int                     data_dma_ch;
>> -       struct pxa_dma_desc     *data_desc;
>> +       dma_addr_t              data_buff_phys;
>>        dma_addr_t              data_desc_addr;
>> +       struct pxa_dma_desc     *data_desc;
>>
>> -       uint32_t                reg_ndcr;
>> -
>> -       /* saved column/page_addr during CMD_SEQIN */
>> -       int                     seqin_column;
>> -       int                     seqin_page_addr;
>> +       struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
>> +       uint32_t                command;
>> +       uint16_t                data_size;      /* data size in FIFO */
>> +       uint16_t                oob_size;
>> +       unsigned char           *data_buff;
>> +       unsigned char           *oob_buff;
>> +       uint32_t                buf_start;
>> +       uint32_t                buf_count;
>>
>>        /* relate to the command */
>>        unsigned int            state;
>> -
>> +       uint8_t                 chip_select;
>>        int                     use_ecc;        /* use HW ECC ? */
>>        int                     use_dma;        /* use DMA ? */
>>        int                     is_ready;
>> -
>> -       unsigned int            page_size;      /* page size of attached chip */
>> -       unsigned int            data_size;      /* data size in FIFO */
>>        int                     retcode;
>> -       struct completion       cmd_complete;
>>
>>        /* generated NDCBx register values */
>> +       uint8_t                 total_cmds;
>>        uint32_t                ndcb0;
>>        uint32_t                ndcb1;
>>        uint32_t                ndcb2;
>> -
>> -       /* timing calcuted from setting */
>> -       uint32_t                ndtr0cs0;
>> -       uint32_t                ndtr1cs0;
>> -
>> -       /* calculated from pxa3xx_nand_flash data */
>> -       size_t          oob_size;
>> -       size_t          read_id_bytes;
>> -
>> -       unsigned int    col_addr_cycles;
>> -       unsigned int    row_addr_cycles;
>>  };
>>
>>  static int use_dma = 1;
>> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>>  /* Define a default flash type setting serve as flash detecting only */
>>  #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>>
>> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>>
>>  #define NDTR0_tCH(c)   (min((c), 7) << 19)
>>  #define NDTR0_tCS(c)   (min((c), 7) << 16)
>> @@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>>  static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>>                                   const struct pxa3xx_nand_timing *t)
>>  {
>> -       unsigned long nand_clk = clk_get_rate(info->clk);
>> +       struct pxa3xx_nand *nand = info->nand_data;
>> +       unsigned long nand_clk;
>>        uint32_t ndtr0, ndtr1;
>>
>> +       nand_clk = clk_get_rate(nand->clk);
>>        ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
>>                NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
>>                NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
>> @@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>>
>>        info->ndtr0cs0 = ndtr0;
>>        info->ndtr1cs0 = ndtr1;
>> -       nand_writel(info, NDTR0CS0, ndtr0);
>> -       nand_writel(info, NDTR1CS0, ndtr1);
>> +       nand_writel(nand, NDTR0CS0, ndtr0);
>> +       nand_writel(nand, NDTR1CS0, ndtr1);
>>  }
>>
>>  static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>>  {
>> +       struct pxa3xx_nand *nand = info->nand_data;
>>        int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
>>
>> -       info->data_size = info->page_size;
>> +       nand->data_size = info->page_size;
>>        if (!oob_enable) {
>> -               info->oob_size = 0;
>> +               nand->oob_size = 0;
>>                return;
>>        }
>>
>>        switch (info->page_size) {
>>        case 2048:
>> -               info->oob_size = (info->use_ecc) ? 40 : 64;
>> +               nand->oob_size = (info->use_ecc) ? 40 : 64;
>>                break;
>>        case 512:
>> -               info->oob_size = (info->use_ecc) ? 8 : 16;
>> +               nand->oob_size = (info->use_ecc) ? 8 : 16;
>>                break;
>>        }
>>  }
>> @@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>>  * 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_info *info)
>> +static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
>>  {
>> +       struct pxa3xx_nand_info *info;
>>        uint32_t ndcr;
>>
>> +       info = nand->info[nand->chip_select];
>>        ndcr = info->reg_ndcr;
>> -       ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
>> -       ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
>> +       ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
>> +       ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
>>        ndcr |= NDCR_ND_RUN;
>>
>>        /* clear status bits and run */
>> -       nand_writel(info, NDCR, 0);
>> -       nand_writel(info, NDSR, NDSR_MASK);
>> -       nand_writel(info, NDCR, ndcr);
>> +       nand_writel(nand, NDCR, 0);
>> +       nand_writel(nand, NDSR, NDSR_MASK);
>> +       nand_writel(nand, NDCR, ndcr);
>>  }
>>
>> -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
>> +static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
>>  {
>>        uint32_t ndcr;
>>        int timeout = NAND_STOP_DELAY;
>>
>>        /* wait RUN bit in NDCR become 0 */
>> -       ndcr = nand_readl(info, NDCR);
>> +       ndcr = nand_readl(nand, NDCR);
>>        while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
>> -               ndcr = nand_readl(info, NDCR);
>> +               ndcr = nand_readl(nand, NDCR);
>>                udelay(1);
>>        }
>>
>>        if (timeout <= 0) {
>>                ndcr &= ~NDCR_ND_RUN;
>> -               nand_writel(info, NDCR, ndcr);
>> +               nand_writel(nand, NDCR, ndcr);
>>        }
>>        /* clear status bits */
>> -       nand_writel(info, NDSR, NDSR_MASK);
>> +       nand_writel(nand, NDSR, NDSR_MASK);
>>  }
>>
>> -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>> +static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>>  {
>>        uint32_t ndcr;
>>
>> -       ndcr = nand_readl(info, NDCR);
>> -       nand_writel(info, NDCR, ndcr & ~int_mask);
>> +       ndcr = nand_readl(nand, NDCR);
>> +       nand_writel(nand, NDCR, ndcr & ~int_mask);
>>  }
>>
>> -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>> +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>>  {
>>        uint32_t ndcr;
>>
>> -       ndcr = nand_readl(info, NDCR);
>> -       nand_writel(info, NDCR, ndcr | int_mask);
>> +       ndcr = nand_readl(nand, NDCR);
>> +       nand_writel(nand, NDCR, ndcr | int_mask);
>>  }
>>
>> -static void handle_data_pio(struct pxa3xx_nand_info *info)
>> +static void handle_data_pio(struct pxa3xx_nand *nand)
>>  {
>> -       switch (info->state) {
>> +       switch (nand->state) {
>>        case STATE_PIO_WRITING:
>> -               __raw_writesl(info->mmio_base + NDDB, info->data_buff,
>> -                               DIV_ROUND_UP(info->data_size, 4));
>> -               if (info->oob_size > 0)
>> -                       __raw_writesl(info->mmio_base + NDDB, info->oob_buff,
>> -                                       DIV_ROUND_UP(info->oob_size, 4));
>> +               __raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
>> +                               DIV_ROUND_UP(nand->data_size, 4));
>> +               if (nand->oob_size > 0)
>> +                       __raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
>> +                                       DIV_ROUND_UP(nand->oob_size, 4));
>>                break;
>>        case STATE_PIO_READING:
>> -               __raw_readsl(info->mmio_base + NDDB, info->data_buff,
>> -                               DIV_ROUND_UP(info->data_size, 4));
>> -               if (info->oob_size > 0)
>> -                       __raw_readsl(info->mmio_base + NDDB, info->oob_buff,
>> -                                       DIV_ROUND_UP(info->oob_size, 4));
>> +               __raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
>> +                               DIV_ROUND_UP(nand->data_size, 4));
>> +               if (nand->oob_size > 0)
>> +                       __raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
>> +                                       DIV_ROUND_UP(nand->oob_size, 4));
>>                break;
>>        default:
>>                printk(KERN_ERR "%s: invalid state %d\n", __func__,
>> -                               info->state);
>> +                               nand->state);
>
> Can't you use dev_err() here?

Good suggestion, I would make this change.
>
>>                BUG();
>
> Is crashing the entire kernel really necessary here?
>
>>        }
>>  }
>>
>> -static void start_data_dma(struct pxa3xx_nand_info *info)
>> +static void start_data_dma(struct pxa3xx_nand *nand)
>>  {
>> -       struct pxa_dma_desc *desc = info->data_desc;
>> -       int dma_len = ALIGN(info->data_size + info->oob_size, 32);
>> +       struct pxa_dma_desc *desc = nand->data_desc;
>> +       int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
>>
>>        desc->ddadr = DDADR_STOP;
>>        desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
>>
>> -       switch (info->state) {
>> +       switch (nand->state) {
>>        case STATE_DMA_WRITING:
>> -               desc->dsadr = info->data_buff_phys;
>> -               desc->dtadr = info->mmio_phys + NDDB;
>> +               desc->dsadr = nand->data_buff_phys;
>> +               desc->dtadr = nand->mmio_phys + NDDB;
>>                desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
>>                break;
>>        case STATE_DMA_READING:
>> -               desc->dtadr = info->data_buff_phys;
>> -               desc->dsadr = info->mmio_phys + NDDB;
>> +               desc->dtadr = nand->data_buff_phys;
>> +               desc->dsadr = nand->mmio_phys + NDDB;
>>                desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
>>                break;
>>        default:
>>                printk(KERN_ERR "%s: invalid state %d\n", __func__,
>> -                               info->state);
>> +                               nand->state);
>>                BUG();
>>        }
>>
>> -       DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
>> -       DDADR(info->data_dma_ch) = info->data_desc_addr;
>> -       DCSR(info->data_dma_ch) |= DCSR_RUN;
>> +       DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
>> +       DDADR(nand->data_dma_ch) = nand->data_desc_addr;
>> +       DCSR(nand->data_dma_ch) |= DCSR_RUN;
>>  }
>>
>>  static void pxa3xx_nand_data_dma_irq(int channel, void *data)
>>  {
>> -       struct pxa3xx_nand_info *info = data;
>> +       struct pxa3xx_nand *nand = data;
>>        uint32_t dcsr;
>>
>>        dcsr = DCSR(channel);
>>        DCSR(channel) = dcsr;
>>
>>        if (dcsr & DCSR_BUSERR) {
>> -               info->retcode = ERR_DMABUSERR;
>> +               nand->retcode = ERR_DMABUSERR;
>>        }
>>
>> -       info->state = STATE_DMA_DONE;
>> -       enable_int(info, NDCR_INT_MASK);
>> -       nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>> +       nand->state = STATE_DMA_DONE;
>> +       enable_int(nand, NDCR_INT_MASK);
>> +       nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>>  }
>>
>>  static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>>  {
>> -       struct pxa3xx_nand_info *info = devid;
>> -       unsigned int status, is_completed = 0;
>> +       struct pxa3xx_nand *nand = devid;
>> +       struct pxa3xx_nand_info *info;
>> +       unsigned int status, is_completed = 0, cs;
>> +       unsigned int ready, cmd_done, page_done, badblock_detect;
>>
>> -       status = nand_readl(info, NDSR);
>> +       cs              = nand->chip_select;
>> +       ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
>> +       cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
>> +       page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
>> +       badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
>> +       info            = nand->info[cs];
>>
>> +       status = nand_readl(nand, NDSR);
>>        if (status & NDSR_DBERR)
>> -               info->retcode = ERR_DBERR;
>> +               nand->retcode = ERR_DBERR;
>>        if (status & NDSR_SBERR)
>> -               info->retcode = ERR_SBERR;
>> +               nand->retcode = ERR_SBERR;
>>        if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
>>                /* whether use dma to transfer data */
>> -               if (info->use_dma) {
>> -                       disable_int(info, NDCR_INT_MASK);
>> -                       info->state = (status & NDSR_RDDREQ) ?
>> +               if (nand->use_dma) {
>> +                       disable_int(nand, NDCR_INT_MASK);
>> +                       nand->state = (status & NDSR_RDDREQ) ?
>>                                      STATE_DMA_READING : STATE_DMA_WRITING;
>> -                       start_data_dma(info);
>> +                       start_data_dma(nand);
>>                        goto NORMAL_IRQ_EXIT;
>>                } else {
>> -                       info->state = (status & NDSR_RDDREQ) ?
>> +                       nand->state = (status & NDSR_RDDREQ) ?
>>                                      STATE_PIO_READING : STATE_PIO_WRITING;
>> -                       handle_data_pio(info);
>> +                       handle_data_pio(nand);
>>                }
>>        }
>> -       if (status & NDSR_CS0_CMDD) {
>> -               info->state = STATE_CMD_DONE;
>> +       if (status & cmd_done) {
>> +               nand->state = STATE_CMD_DONE;
>>                is_completed = 1;
>>        }
>> -       if (status & NDSR_FLASH_RDY) {
>> -               info->is_ready = 1;
>> -               info->state = STATE_READY;
>> +       if (status & ready) {
>> +               nand->is_ready = 1;
>> +               nand->state = STATE_READY;
>>        }
>>
>>        if (status & NDSR_WRCMDREQ) {
>> -               nand_writel(info, NDSR, NDSR_WRCMDREQ);
>> +               nand_writel(nand, NDSR, NDSR_WRCMDREQ);
>>                status &= ~NDSR_WRCMDREQ;
>> -               info->state = STATE_CMD_HANDLE;
>> -               nand_writel(info, NDCB0, info->ndcb0);
>> -               nand_writel(info, NDCB0, info->ndcb1);
>> -               nand_writel(info, NDCB0, info->ndcb2);
>> +               nand->state = STATE_CMD_HANDLE;
>> +               nand_writel(nand, NDCB0, nand->ndcb0);
>> +               nand_writel(nand, NDCB0, nand->ndcb1);
>> +               nand_writel(nand, NDCB0, nand->ndcb2);
>>        }
>>
>>        /* clear NDSR to let the controller exit the IRQ */
>> -       nand_writel(info, NDSR, status);
>> +       nand_writel(nand, NDSR, status);
>>        if (is_completed)
>> -               complete(&info->cmd_complete);
>> +               complete(&nand->cmd_complete);
>>  NORMAL_IRQ_EXIT:
>>        return IRQ_HANDLED;
>>  }
>>
>> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
>> -{
>> -       struct pxa3xx_nand_info *info = mtd->priv;
>> -       return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
>> -}
>> -
>>  static inline int is_buf_blank(uint8_t *buf, size_t len)
>>  {
>>        for (; len > 0; len--)
>> @@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
>>        return 1;
>>  }
>>
>> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>>                uint16_t column, int page_addr)
>>  {
>>        uint16_t cmd;
>>        int addr_cycle, exec_cmd, ndcb0;
>> -       struct mtd_info *mtd = info->mtd;
>> +       struct mtd_info *mtd;
>> +       struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>>
>> -       ndcb0 = 0;
>> +       mtd = get_mtd_by_info(info);
>> +       ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
>>        addr_cycle = 0;
>>        exec_cmd = 1;
>>
>>        /* reset data and oob column point to handle data */
>> -       info->buf_start         = 0;
>> -       info->buf_count         = 0;
>> -       info->oob_size          = 0;
>> -       info->use_ecc           = 0;
>> -       info->is_ready          = 0;
>> -       info->retcode           = ERR_NONE;
>> +       nand->buf_start         = 0;
>> +       nand->buf_count         = 0;
>> +       nand->oob_size          = 0;
>> +       nand->use_ecc           = 0;
>> +       nand->is_ready          = 0;
>> +       nand->retcode           = ERR_NONE;
>> +       nand->data_size         = 0;
>> +       nand->use_dma           = 0;
>> +       nand->command           = command;
>>
>>        switch (command) {
>>        case NAND_CMD_READ0:
>>        case NAND_CMD_PAGEPROG:
>> -               info->use_ecc = 1;
>> +               nand->use_ecc = 1;
>>        case NAND_CMD_READOOB:
>>                pxa3xx_set_datasize(info);
>> +               nand->oob_buff = nand->data_buff + nand->data_size;
>> +               nand->use_dma = use_dma;
>>                break;
>>        case NAND_CMD_SEQIN:
>>                exec_cmd = 0;
>>                break;
>>        default:
>> -               info->ndcb1 = 0;
>> -               info->ndcb2 = 0;
>> +               nand->ndcb1 = 0;
>> +               nand->ndcb2 = 0;
>>                break;
>>        }
>>
>> -       info->ndcb0 = ndcb0;
>> +       nand->ndcb0 = ndcb0;
>>        addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
>>                                    + info->col_addr_cycles);
>>
>> @@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>        case NAND_CMD_READ0:
>>                cmd = info->cmdset->read1;
>>                if (command == NAND_CMD_READOOB)
>> -                       info->buf_start = mtd->writesize + column;
>> +                       nand->buf_start = mtd->writesize + column;
>>                else
>> -                       info->buf_start = column;
>> +                       nand->buf_start = column;
>>
>>                if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
>> -                       info->ndcb0 |= NDCB0_CMD_TYPE(0)
>> +                       nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>>                                        | addr_cycle
>>                                        | (cmd & NDCB0_CMD1_MASK);
>>                else
>> -                       info->ndcb0 |= NDCB0_CMD_TYPE(0)
>> +                       nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>>                                        | NDCB0_DBC
>>                                        | addr_cycle
>>                                        | cmd;
>> @@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>        case NAND_CMD_SEQIN:
>>                /* small page addr setting */
>>                if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
>> -                       info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>> +                       nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>>                                        | (column & 0xFF);
>>
>> -                       info->ndcb2 = 0;
>> +                       nand->ndcb2 = 0;
>>                } else {
>> -                       info->ndcb1 = ((page_addr & 0xFFFF) << 16)
>> +                       nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
>>                                        | (column & 0xFFFF);
>>
>>                        if (page_addr & 0xFF0000)
>> -                               info->ndcb2 = (page_addr & 0xFF0000) >> 16;
>> +                               nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
>>                        else
>> -                               info->ndcb2 = 0;
>> +                               nand->ndcb2 = 0;
>>                }
>>
>> -               info->buf_count = mtd->writesize + mtd->oobsize;
>> -               memset(info->data_buff, 0xFF, info->buf_count);
>> +               nand->buf_count = mtd->writesize + mtd->oobsize;
>> +               memset(nand->data_buff, 0xFF, nand->buf_count);
>>
>>                break;
>>
>>        case NAND_CMD_PAGEPROG:
>> -               if (is_buf_blank(info->data_buff,
>> +               if (is_buf_blank(nand->data_buff,
>>                                        (mtd->writesize + mtd->oobsize))) {
>>                        exec_cmd = 0;
>>                        break;
>>                }
>>
>>                cmd = info->cmdset->program;
>> -               info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>> +               nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>>                                | NDCB0_AUTO_RS
>>                                | NDCB0_ST_ROW_EN
>>                                | NDCB0_DBC
>> @@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>
>>        case NAND_CMD_READID:
>>                cmd = info->cmdset->read_id;
>> -               info->buf_count = info->read_id_bytes;
>> -               info->ndcb0 |= NDCB0_CMD_TYPE(3)
>> +               nand->buf_count = info->read_id_bytes;
>> +               nand->ndcb0 |= NDCB0_CMD_TYPE(3)
>>                                | NDCB0_ADDR_CYC(1)
>>                                | cmd;
>>
>> -               info->data_size = 8;
>> +               nand->data_size = 8;
>>                break;
>>        case NAND_CMD_STATUS:
>>                cmd = info->cmdset->read_status;
>> -               info->buf_count = 1;
>> -               info->ndcb0 |= NDCB0_CMD_TYPE(4)
>> +               nand->buf_count = 1;
>> +               nand->ndcb0 |= NDCB0_CMD_TYPE(4)
>>                                | NDCB0_ADDR_CYC(1)
>>                                | cmd;
>>
>> -               info->data_size = 8;
>> +               nand->data_size = 8;
>>                break;
>>
>>        case NAND_CMD_ERASE1:
>>                cmd = info->cmdset->erase;
>> -               info->ndcb0 |= NDCB0_CMD_TYPE(2)
>> +               nand->ndcb0 |= NDCB0_CMD_TYPE(2)
>>                                | NDCB0_AUTO_RS
>>                                | NDCB0_ADDR_CYC(3)
>>                                | NDCB0_DBC
>>                                | cmd;
>> -               info->ndcb1 = page_addr;
>> -               info->ndcb2 = 0;
>> +               nand->ndcb1 = page_addr;
>> +               nand->ndcb2 = 0;
>>
>>                break;
>>        case NAND_CMD_RESET:
>>                cmd = info->cmdset->reset;
>> -               info->ndcb0 |= NDCB0_CMD_TYPE(5)
>> +               nand->ndcb0 |= NDCB0_CMD_TYPE(5)
>>                                | cmd;
>>
>>                break;
>> @@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>>                                int column, int page_addr)
>>  {
>>        struct pxa3xx_nand_info *info = mtd->priv;
>> +       struct pxa3xx_nand *nand = info->nand_data;
>>        int ret, exec_cmd;
>>
>>        /*
>> @@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>>        if (info->reg_ndcr & NDCR_DWIDTH_M)
>>                column /= 2;
>>
>> -       exec_cmd = prepare_command_pool(info, command, column, page_addr);
>> +       /*
>> +        * There may be different NAND chip hooked to
>> +        * different chip select, so check whether
>> +        * chip select has been changed, if yes, reset the timing
>> +        */
>> +       if (nand->chip_select != info->chip_select) {
>> +               nand->chip_select = info->chip_select;
>> +               nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>> +               nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>> +       }
>> +
>> +       nand->state = STATE_PREPARED;
>> +       exec_cmd = prepare_command_pool(nand, command, column, page_addr);
>>        if (exec_cmd) {
>> -               init_completion(&info->cmd_complete);
>> -               pxa3xx_nand_start(info);
>> +               init_completion(&nand->cmd_complete);
>> +               pxa3xx_nand_start(nand);
>>
>> -               ret = wait_for_completion_timeout(&info->cmd_complete,
>> +               ret = wait_for_completion_timeout(&nand->cmd_complete,
>>                                CHIP_DELAY_TIMEOUT);
>>                if (!ret) {
>>                        printk(KERN_ERR "Wait time out!!!\n");
>>                        /* Stop State Machine for next command cycle */
>> -                       pxa3xx_nand_stop(info);
>> +                       pxa3xx_nand_stop(nand);
>>                }
>> -               info->state = STATE_IDLE;
>>        }
>> +       nand->state = STATE_IDLE;
>>  }
>>
>>  static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
>> @@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>>                struct nand_chip *chip, uint8_t *buf, int page)
>>  {
>>        struct pxa3xx_nand_info *info = mtd->priv;
>> +       struct pxa3xx_nand *nand = info->nand_data;
>>
>>        chip->read_buf(mtd, buf, mtd->writesize);
>>        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>>
>> -       if (info->retcode == ERR_SBERR) {
>> +       if (nand->retcode == ERR_SBERR) {
>>                switch (info->use_ecc) {
>>                case 1:
>>                        mtd->ecc_stats.corrected++;
>> @@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>>                default:
>>                        break;
>>                }
>> -       } else if (info->retcode == ERR_DBERR) {
>> +       } else if (nand->retcode == ERR_DBERR) {
>>                /*
>>                 * for blank page (all 0xff), HW will calculate its ECC as
>>                 * 0, which is different from the ECC information within
>>                 * OOB, ignore such double bit errors
>>                 */
>>                if (is_buf_blank(buf, mtd->writesize))
>> -                       info->retcode = ERR_NONE;
>> +                       nand->retcode = ERR_NONE;
>>                else
>>                        mtd->ecc_stats.failed++;
>>        }
>> @@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>>  static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>>  {
>>        struct pxa3xx_nand_info *info = mtd->priv;
>> +       struct pxa3xx_nand *nand = info->nand_data;
>>        char retval = 0xFF;
>>
>> -       if (info->buf_start < info->buf_count)
>> +       if (nand->buf_start < nand->buf_count)
>>                /* Has just send a new command? */
>> -               retval = info->data_buff[info->buf_start++];
>> +               retval = nand->data_buff[nand->buf_start++];
>>
>>        return retval;
>>  }
>> @@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>>  static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>>  {
>>        struct pxa3xx_nand_info *info = mtd->priv;
>> +       struct pxa3xx_nand *nand = info->nand_data;
>>        u16 retval = 0xFFFF;
>>
>> -       if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
>> -               retval = *((u16 *)(info->data_buff+info->buf_start));
>> -               info->buf_start += 2;
>> +       if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
>> +               retval = *((u16 *)(nand->data_buff+nand->buf_start));
>> +               nand->buf_start += 2;
>>        }
>>        return retval;
>>  }
>> @@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>>  static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>>  {
>>        struct pxa3xx_nand_info *info = mtd->priv;
>> -       int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>> +       struct pxa3xx_nand *nand = info->nand_data;
>> +       int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>>
>> -       memcpy(buf, info->data_buff + info->buf_start, real_len);
>> -       info->buf_start += real_len;
>> +       memcpy(buf, nand->data_buff + nand->buf_start, real_len);
>> +       nand->buf_start += real_len;
>>  }
>>
>>  static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
>>                const uint8_t *buf, int len)
>>  {
>>        struct pxa3xx_nand_info *info = mtd->priv;
>> -       int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>> +       struct pxa3xx_nand *nand = info->nand_data;
>> +       int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>>
>> -       memcpy(info->data_buff + info->buf_start, buf, real_len);
>> -       info->buf_start += real_len;
>> +       memcpy(nand->data_buff + nand->buf_start, buf, real_len);
>> +       nand->buf_start += real_len;
>>  }
>>
>>  static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
>> @@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>>  static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>>  {
>>        struct pxa3xx_nand_info *info = mtd->priv;
>> +       struct pxa3xx_nand *nand = info->nand_data;
>>
>>        /* pxa3xx_nand_send_command has waited for command complete */
>>        if (this->state == FL_WRITING || this->state == FL_ERASING) {
>> -               if (info->retcode == ERR_NONE)
>> +               if (nand->retcode == ERR_NONE)
>>                        return 0;
>>                else {
>>                        /*
>> @@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>>  static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>>                                    const struct pxa3xx_nand_flash *f)
>>  {
>> -       struct platform_device *pdev = info->pdev;
>> +       struct pxa3xx_nand *nand = info->nand_data;
>> +       struct platform_device *pdev = nand->pdev;
>>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>        uint32_t ndcr = 0x0; /* enable all interrupts */
>>
>> @@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>>        ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>>
>>        info->reg_ndcr = ndcr;
>> +       info->use_ecc = 1;
>>
>>        pxa3xx_nand_set_timing(info, f->timing);
>>        return 0;
>> @@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>>
>>  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>>  {
>> -       uint32_t ndcr = nand_readl(info, NDCR);
>> +       struct pxa3xx_nand *nand = info->nand_data;
>> +       uint32_t ndcr = nand_readl(nand, NDCR);
>> +
>> +       if (info->chip_select > 0) {
>> +               printk(KERN_ERR "We could not detect configure"
>> +                               " if more than one cs is supported!!\n");
>> +               BUG();
>> +       }
>>        info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
>>        /* set info fields needed to read id */
>>        info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
>>        info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
>>        info->cmdset = &default_cmdset;
>>
>> -       info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
>> -       info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
>> +       info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
>> +       info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
>>
>>        return 0;
>>  }
>> @@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>>  */
>>  #define MAX_BUFF_SIZE  PAGE_SIZE
>>
>> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
>> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
>>  {
>> -       struct platform_device *pdev = info->pdev;
>> -       int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>> -
>> -       if (use_dma == 0) {
>> -               info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
>> -               if (info->data_buff == NULL)
>> -                       return -ENOMEM;
>> -               return 0;
>> -       }
>> -
>> -       info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
>> -                               &info->data_buff_phys, GFP_KERNEL);
>> -       if (info->data_buff == NULL) {
>> -               dev_err(&pdev->dev, "failed to allocate dma buffer\n");
>> -               return -ENOMEM;
>> -       }
>> -
>> -       info->data_buff_size = MAX_BUFF_SIZE;
>> -       info->data_desc = (void *)info->data_buff + data_desc_offset;
>> -       info->data_desc_addr = info->data_buff_phys + data_desc_offset;
>> +       struct pxa3xx_nand *nand;
>> +       struct mtd_info *mtd;
>>
>> -       info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
>> -                               pxa3xx_nand_data_dma_irq, info);
>> -       if (info->data_dma_ch < 0) {
>> -               dev_err(&pdev->dev, "failed to request data dma\n");
>> -               dma_free_coherent(&pdev->dev, info->data_buff_size,
>> -                               info->data_buff, info->data_buff_phys);
>> -               return info->data_dma_ch;
>> -       }
>> +       if (!info)
>> +               return;
>>
>> -       return 0;
>> +       nand = info->nand_data;
>> +       mtd = get_mtd_by_info(info);
>> +       kfree(mtd);
>> +       nand->info[cs] = NULL;
>>  }
>>
>>  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>>  {
>> -       struct mtd_info *mtd = info->mtd;
>> +       struct pxa3xx_nand *nand = info->nand_data;
>> +       struct mtd_info *mtd = get_mtd_by_info(info);
>>        struct nand_chip *chip = mtd->priv;
>>
>>        /* use the common timing to make a try */
>> -       pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
>> +       if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
>> +               return 0;
>>        chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
>> -       if (info->is_ready)
>> +       if (nand->is_ready)
>>                return 1;
>>        else
>>                return 0;
>> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>>  static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>  {
>>        struct pxa3xx_nand_info *info = mtd->priv;
>> -       struct platform_device *pdev = info->pdev;
>> +       struct pxa3xx_nand *nand = info->nand_data;
>> +       struct platform_device *pdev = nand->pdev;
>>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>        struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>>        const struct pxa3xx_nand_flash *f = NULL;
>> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>        uint64_t chipsize;
>>        int i, ret, num;
>>
>> +       nand->chip_select = info->chip_select;
>>        if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>>                goto KEEP_CONFIG;
>>
>>        ret = pxa3xx_nand_sensing(info);
>>        if (!ret) {
>> -               kfree(mtd);
>> -               info->mtd = NULL;
>> -               printk(KERN_INFO "There is no nand chip on cs 0!\n");
>> +               free_cs_resource(info, nand->chip_select);
>> +               printk(KERN_INFO "There is no nand chip on cs %d!\n",
>> +                               nand->chip_select);
>
> dev_err()?
>
>>
>>                return -EINVAL;
>>        }
>>
>>        chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>> -       id = *((uint16_t *)(info->data_buff));
>> +       id = *((uint16_t *)(nand->data_buff));
>>        if (id != 0)
>>                printk(KERN_INFO "Detect a flash id %x\n", id);
>>        else {
>> -               kfree(mtd);
>> -               info->mtd = NULL;
>> -               printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
>> -
>> +               free_cs_resource(info, nand->chip_select);
>>                return -EINVAL;
>>        }
>>
>> @@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>        }
>>
>>        if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
>> -               kfree(mtd);
>> -               info->mtd = NULL;
>> +               free_cs_resource(info, nand->chip_select);
>>                printk(KERN_ERR "ERROR!! flash not defined!!!\n");
>
> Also here.
>
>>
>>                return -EINVAL;
>>        }
>>
>> -       pxa3xx_nand_config_flash(info, f);
>> +       if (pxa3xx_nand_config_flash(info, f)) {
>> +               printk(KERN_ERR "ERROR! Configure failed\n");
>
> And here. Giving no hint about which driver or function causes this
> message makes debugging harder than it should be.
>
> [...]
>
>>  #ifdef CONFIG_PM
>> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>        struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>        struct mtd_info *mtd = info->mtd;
>>
>> -       nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>> -       nand_writel(info, NDTR1CS0, info->ndtr1cs0);
>> +       nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>> +       nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>>        clk_enable(info->clk);
>
> This won't compile.

Could you post your build log?

Thanks,
Lei

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-08 17:38     ` Lei Wen
@ 2011-06-22 13:06       ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-06-22 13:06 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

Hi Lei,

On Wed, Jun 22, 2011 at 2:21 PM, Lei Wen <adrian.wenl@gmail.com> wrote:
> Hi Daniel,
>
> On Wed, Jun 22, 2011 at 7:39 PM, Daniel Mack <zonque@gmail.com> wrote:
>> On Wed, Jun 22, 2011 at 5:17 AM, Lei Wen <leiwen@marvell.com> wrote:
>>> Current pxa3xx_nand controller has two chip select which
>>> both be workable. This patch enable this feature.
>>>
>>> Update platform driver to support this feature.
>>>
>>> Another 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.
>>>
>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>> ---
>>>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>>>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>>>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>>>  arch/arm/mach-pxa/littleton.c                |    5 +-
>>>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>>>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>>>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>>>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>>>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>>>  9 files changed, 444 insertions(+), 338 deletions(-)
>>
>> This patch doesn't apply for me on top of Linus' master branch. Which
>> tree are you based on currently?
>
> Actually it is based on Linux 3.0-rc2 and add two patches pending to
> l2-mtd-2.6.git already:
>
> mtd: pxa3xx_nand: fix nand detection issue
> mtd: pxa3xx_nand: Fix blank page ECC mismatch

I have a similar setup, also with those two patches on top, but
applying your patch gives me:

Applying: MTD: pxa3xx_nand: enable multiple chip select support
error: patch failed: drivers/mtd/nand/pxa3xx_nand.c:477
error: drivers/mtd/nand/pxa3xx_nand.c: patch does not apply
Patch failed at 0001 MTD: pxa3xx_nand: enable multiple chip select support
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

Could you push your branch somewhere?

>>>  #ifdef CONFIG_PM
>>> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>        struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>        struct mtd_info *mtd = info->mtd;
>>>
>>> -       nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>>> -       nand_writel(info, NDTR1CS0, info->ndtr1cs0);
>>> +       nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>>> +       nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>>>        clk_enable(info->clk);
>>
>> This won't compile.
>
> Could you post your build log?

I didn't compile it because I couldn't, but "nand" is certainly
undefined in this context now. Did you compile and test this code with
"CONFIG_PM=y"?


Thanks,
Daniel

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-22 13:06       ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-06-22 13:06 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lei,

On Wed, Jun 22, 2011 at 2:21 PM, Lei Wen <adrian.wenl@gmail.com> wrote:
> Hi Daniel,
>
> On Wed, Jun 22, 2011 at 7:39 PM, Daniel Mack <zonque@gmail.com> wrote:
>> On Wed, Jun 22, 2011 at 5:17 AM, Lei Wen <leiwen@marvell.com> wrote:
>>> Current pxa3xx_nand controller has two chip select which
>>> both be workable. This patch enable this feature.
>>>
>>> Update platform driver to support this feature.
>>>
>>> Another 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.
>>>
>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>> ---
>>> ?arch/arm/mach-mmp/aspenite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>>> ?arch/arm/mach-pxa/cm-x300.c ? ? ? ? ? ? ? ? ?| ? ?5 +-
>>> ?arch/arm/mach-pxa/colibri-pxa3xx.c ? ? ? ? ? | ? ?5 +-
>>> ?arch/arm/mach-pxa/littleton.c ? ? ? ? ? ? ? ?| ? ?5 +-
>>> ?arch/arm/mach-pxa/mxm8x10.c ? ? ? ? ? ? ? ? ?| ? ?9 +-
>>> ?arch/arm/mach-pxa/raumfeld.c ? ? ? ? ? ? ? ? | ? ?5 +-
>>> ?arch/arm/mach-pxa/zylonite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>>> ?arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | ? ?8 +-
>>> ?drivers/mtd/nand/pxa3xx_nand.c ? ? ? ? ? ? ? | ?735 +++++++++++++++-----------
>>> ?9 files changed, 444 insertions(+), 338 deletions(-)
>>
>> This patch doesn't apply for me on top of Linus' master branch. Which
>> tree are you based on currently?
>
> Actually it is based on Linux 3.0-rc2 and add two patches pending to
> l2-mtd-2.6.git already:
>
> mtd: pxa3xx_nand: fix nand detection issue
> mtd: pxa3xx_nand: Fix blank page ECC mismatch

I have a similar setup, also with those two patches on top, but
applying your patch gives me:

Applying: MTD: pxa3xx_nand: enable multiple chip select support
error: patch failed: drivers/mtd/nand/pxa3xx_nand.c:477
error: drivers/mtd/nand/pxa3xx_nand.c: patch does not apply
Patch failed at 0001 MTD: pxa3xx_nand: enable multiple chip select support
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

Could you push your branch somewhere?

>>> ?#ifdef CONFIG_PM
>>> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>> ? ? ? ?struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>> ? ? ? ?struct mtd_info *mtd = info->mtd;
>>>
>>> - ? ? ? nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>>> - ? ? ? nand_writel(info, NDTR1CS0, info->ndtr1cs0);
>>> + ? ? ? nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>>> + ? ? ? nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>>> ? ? ? ?clk_enable(info->clk);
>>
>> This won't compile.
>
> Could you post your build log?

I didn't compile it because I couldn't, but "nand" is certainly
undefined in this context now. Did you compile and test this code with
"CONFIG_PM=y"?


Thanks,
Daniel

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-08 16:00 ` Lei Wen
@ 2011-07-08 16:09   ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-06-22 13:45 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Haojian Zhuang,
	linux-mtd, linux-arm-kernel

Hi Lei,


Some comments from a quick look...


On 06/22/11 06:17, Lei Wen wrote:

> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
>
> Update platform driver to support this feature.
>
> Another 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.

You should _also_ put this comment inside the pxa3xx_nand.h
may be even inside the pxa3xx_nand_platform_data structure,
so people would not have to search the git log to find this problem.

> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>  9 files changed, 444 insertions(+), 338 deletions(-)
>
> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
> index 06b5fa8..b6589d6 100644
> --- a/arch/arm/mach-mmp/aspenite.c
> +++ b/arch/arm/mach-mmp/aspenite.c
> @@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
>  
>  static struct pxa3xx_nand_platform_data aspenite_nand_info = {
>  	.enable_arbiter	= 1,
> -	.parts		= aspenite_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
> +	.cs_num = 1,
> +	.parts[0]	= aspenite_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
>  };
>  
>  static struct i2c_board_info aspenite_i2c_info[] __initdata = {
> diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
> index b2248e7..d67eb7b 100644
> --- a/arch/arm/mach-pxa/cm-x300.c
> +++ b/arch/arm/mach-pxa/cm-x300.c
> @@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
>  	.enable_arbiter	= 1,
>  	.keep_config	= 1,
> -	.parts		= cm_x300_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= cm_x300_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
>  };
>  
>  static void __init cm_x300_init_nand(void)
> diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
> index 3f9be41..ff7a07b 100644
> --- a/arch/arm/mach-pxa/colibri-pxa3xx.c
> +++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
> @@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data colibri_nand_info = {
>  	.enable_arbiter	= 1,
>  	.keep_config	= 1,
> -	.parts		= colibri_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= colibri_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
>  };
>  
>  void __init colibri_pxa3xx_init_nand(void)
> diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
> index e5e326d..6eaf852 100644
> --- a/arch/arm/mach-pxa/littleton.c
> +++ b/arch/arm/mach-pxa/littleton.c
> @@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
>  
>  static struct pxa3xx_nand_platform_data littleton_nand_info = {
>  	.enable_arbiter	= 1,
> -	.parts		= littleton_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= littleton_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
>  };
>  
>  static void __init littleton_init_nand(void)
> diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
> index b5a8fd3..e7ce135 100644
> --- a/arch/arm/mach-pxa/mxm8x10.c
> +++ b/arch/arm/mach-pxa/mxm8x10.c
> @@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
>  };
>  
>  static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
> -	.enable_arbiter = 1,
> -	.keep_config = 1,
> -	.parts = mxm_8x10_nand_partitions,
> -	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
> +	.enable_arbiter	= 1,
> +	.keep_config	= 1,
> +	.cs_num		= 1,
> +	.parts[0]	= mxm_8x10_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
>  };
>  
>  static void __init mxm_8x10_nand_init(void)
> diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
> index d130f77..a54846d 100644
> --- a/arch/arm/mach-pxa/raumfeld.c
> +++ b/arch/arm/mach-pxa/raumfeld.c
> @@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
>  	.enable_arbiter	= 1,
>  	.keep_config	= 1,
> -	.parts		= raumfeld_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= raumfeld_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
>  };
>  
>  /**
> diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
> index 5821185..ea4752a 100644
> --- a/arch/arm/mach-pxa/zylonite.c
> +++ b/arch/arm/mach-pxa/zylonite.c
> @@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
>  
>  static struct pxa3xx_nand_platform_data zylonite_nand_info = {
>  	.enable_arbiter	= 1,
> -	.parts		= zylonite_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= zylonite_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
>  };
>  
>  static void __init zylonite_init_nand(void)
> diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> index 442301f..34a3f52 100644
> --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> @@ -41,6 +41,8 @@ struct pxa3xx_nand_flash {
>  	struct pxa3xx_nand_timing *timing;	/* NAND Flash timing */
>  };
>  
> +/* The max num of chip select current support */

/* The maximum number of chip selects currently supported */

> +#define NUM_CHIP_SELECT		(2)
>  struct pxa3xx_nand_platform_data {
>  
>  	/* the data flash bus is shared between the Static Memory
> @@ -52,8 +54,10 @@ struct pxa3xx_nand_platform_data {
>  	/* allow platform code to keep OBM/bootloader defined NFC config */
>  	int	keep_config;
>  
> -	const struct mtd_partition		*parts;
> -	unsigned int				nr_parts;
> +	/* indicate how many chip select would be used for this platform */

/* indicate how many chip selects will be used */

> +	int	cs_num;

This name is too confusing, I think even num_cs is better or cs_count?
Also, may be align it with the others?

> +	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;
> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index 30689cc..259b8d5 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -92,11 +92,13 @@
>  #define NDCB0_ADDR_CYC_SHIFT	(16)
>  
>  /* macros for registers read/write */
> -#define nand_writel(info, off, val)	\
> -	__raw_writel((val), (info)->mmio_base + (off))
> +#define nand_writel(nand, off, val)	\
> +	__raw_writel((val), (nand)->mmio_base + (off))
>  
> -#define nand_readl(info, off)		\
> -	__raw_readl((info)->mmio_base + (off))
> +#define nand_readl(nand, off)		\
> +	__raw_readl((nand)->mmio_base + (off))
> +#define get_mtd_by_info(info)		\
> +	(struct mtd_info *)((void *)info - sizeof(struct mtd_info))
>  
>  /* error code and state */
>  enum {
> @@ -110,6 +112,7 @@ enum {
>  
>  enum {
>  	STATE_IDLE = 0,
> +	STATE_PREPARED,
>  	STATE_CMD_HANDLE,
>  	STATE_DMA_READING,
>  	STATE_DMA_WRITING,
> @@ -123,63 +126,63 @@ enum {
>  struct pxa3xx_nand_info {
>  	struct nand_chip	nand_chip;
>  
> -	struct nand_hw_control	controller;
> -	struct platform_device	 *pdev;
>  	struct pxa3xx_nand_cmdset *cmdset;
> +	/* page size of attached chip */
> +	uint16_t		page_size;
> +	uint8_t			chip_select;
> +	uint8_t			use_ecc;
> +
> +	/* calculated from pxa3xx_nand_flash data */
> +	uint8_t			col_addr_cycles;
> +	uint8_t			row_addr_cycles;
> +	uint8_t			read_id_bytes;
> +
> +	/* cached register value */
> +	uint32_t		reg_ndcr;
> +	uint32_t		ndtr0cs0;
> +	uint32_t		ndtr1cs0;
>  
> +	void			*nand_data;
> +};
> +
> +struct pxa3xx_nand {
>  	struct clk		*clk;
>  	void __iomem		*mmio_base;
>  	unsigned long		mmio_phys;
> +	struct nand_hw_control	controller;
> +	struct completion	cmd_complete;
> +	struct platform_device	 *pdev;

please, align

>  
> -	unsigned int 		buf_start;
> -	unsigned int		buf_count;
> -
> -	struct mtd_info         *mtd;
>  	/* DMA information */
>  	int			drcmr_dat;
>  	int			drcmr_cmd;
> -
> -	unsigned char		*data_buff;
> -	unsigned char		*oob_buff;
> -	dma_addr_t 		data_buff_phys;
> -	size_t			data_buff_size;
>  	int 			data_dma_ch;
> -	struct pxa_dma_desc	*data_desc;
> +	dma_addr_t		data_buff_phys;
>  	dma_addr_t 		data_desc_addr;
> +	struct pxa_dma_desc	*data_desc;
>  
> -	uint32_t		reg_ndcr;
> -
> -	/* saved column/page_addr during CMD_SEQIN */
> -	int			seqin_column;
> -	int			seqin_page_addr;
> +	struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
> +	uint32_t		command;
> +	uint16_t		data_size;	/* data size in FIFO */
> +	uint16_t		oob_size;
> +	unsigned char		*data_buff;
> +	unsigned char		*oob_buff;
> +	uint32_t		buf_start;
> +	uint32_t		buf_count;
>  
>  	/* relate to the command */
>  	unsigned int		state;
> -
> +	uint8_t			chip_select;
>  	int			use_ecc;	/* use HW ECC ? */
>  	int			use_dma;	/* use DMA ? */
>  	int			is_ready;
> -
> -	unsigned int		page_size;	/* page size of attached chip */
> -	unsigned int		data_size;	/* data size in FIFO */
>  	int 			retcode;
> -	struct completion 	cmd_complete;
>  
>  	/* generated NDCBx register values */
> +	uint8_t			total_cmds;
>  	uint32_t		ndcb0;
>  	uint32_t		ndcb1;
>  	uint32_t		ndcb2;
> -
> -	/* timing calcuted from setting */
> -	uint32_t		ndtr0cs0;
> -	uint32_t		ndtr1cs0;
> -
> -	/* calculated from pxa3xx_nand_flash data */
> -	size_t		oob_size;
> -	size_t		read_id_bytes;
> -
> -	unsigned int	col_addr_cycles;
> -	unsigned int	row_addr_cycles;
>  };

It looks like if you switch the names of the structures above,
then the patch will be much shorter and cleaner,
but will it make structures meaning confusion?

>  
>  static int use_dma = 1;
> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>  /* Define a default flash type setting serve as flash detecting only */
>  #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>  
> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>  
>  #define NDTR0_tCH(c)	(min((c), 7) << 19)
>  #define NDTR0_tCS(c)	(min((c), 7) << 16)
> @@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>  static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>  				   const struct pxa3xx_nand_timing *t)
>  {
> -	unsigned long nand_clk = clk_get_rate(info->clk);
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	unsigned long nand_clk;
>  	uint32_t ndtr0, ndtr1;
>  
> +	nand_clk = clk_get_rate(nand->clk);
>  	ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
>  		NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
>  		NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
> @@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>  
>  	info->ndtr0cs0 = ndtr0;
>  	info->ndtr1cs0 = ndtr1;
> -	nand_writel(info, NDTR0CS0, ndtr0);
> -	nand_writel(info, NDTR1CS0, ndtr1);
> +	nand_writel(nand, NDTR0CS0, ndtr0);
> +	nand_writel(nand, NDTR1CS0, ndtr1);
>  }
>  
>  static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>  {
> +	struct pxa3xx_nand *nand = info->nand_data;
>  	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
>  
> -	info->data_size = info->page_size;
> +	nand->data_size = info->page_size;
>  	if (!oob_enable) {
> -		info->oob_size = 0;
> +		nand->oob_size = 0;
>  		return;
>  	}
>  
>  	switch (info->page_size) {
>  	case 2048:
> -		info->oob_size = (info->use_ecc) ? 40 : 64;
> +		nand->oob_size = (info->use_ecc) ? 40 : 64;
>  		break;
>  	case 512:
> -		info->oob_size = (info->use_ecc) ? 8 : 16;
> +		nand->oob_size = (info->use_ecc) ? 8 : 16;
>  		break;
>  	}
>  }
> @@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>   * 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_info *info)
> +static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
>  {
> +	struct pxa3xx_nand_info *info;
>  	uint32_t ndcr;
>  
> +	info = nand->info[nand->chip_select];
>  	ndcr = info->reg_ndcr;
> -	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
> -	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
> +	ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
> +	ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
>  	ndcr |= NDCR_ND_RUN;
>  
>  	/* clear status bits and run */
> -	nand_writel(info, NDCR, 0);
> -	nand_writel(info, NDSR, NDSR_MASK);
> -	nand_writel(info, NDCR, ndcr);
> +	nand_writel(nand, NDCR, 0);
> +	nand_writel(nand, NDSR, NDSR_MASK);
> +	nand_writel(nand, NDCR, ndcr);
>  }
>  
> -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
> +static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
>  {
>  	uint32_t ndcr;
>  	int timeout = NAND_STOP_DELAY;
>  
>  	/* wait RUN bit in NDCR become 0 */
> -	ndcr = nand_readl(info, NDCR);
> +	ndcr = nand_readl(nand, NDCR);
>  	while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
> -		ndcr = nand_readl(info, NDCR);
> +		ndcr = nand_readl(nand, NDCR);
>  		udelay(1);
>  	}
>  
>  	if (timeout <= 0) {
>  		ndcr &= ~NDCR_ND_RUN;
> -		nand_writel(info, NDCR, ndcr);
> +		nand_writel(nand, NDCR, ndcr);
>  	}
>  	/* clear status bits */
> -	nand_writel(info, NDSR, NDSR_MASK);
> +	nand_writel(nand, NDSR, NDSR_MASK);
>  }
>  
> -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> +static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>  {
>  	uint32_t ndcr;
>  
> -	ndcr = nand_readl(info, NDCR);
> -	nand_writel(info, NDCR, ndcr & ~int_mask);
> +	ndcr = nand_readl(nand, NDCR);
> +	nand_writel(nand, NDCR, ndcr & ~int_mask);
>  }
>  
> -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>  {
>  	uint32_t ndcr;
>  
> -	ndcr = nand_readl(info, NDCR);
> -	nand_writel(info, NDCR, ndcr | int_mask);
> +	ndcr = nand_readl(nand, NDCR);
> +	nand_writel(nand, NDCR, ndcr | int_mask);
>  }
>  
> -static void handle_data_pio(struct pxa3xx_nand_info *info)
> +static void handle_data_pio(struct pxa3xx_nand *nand)
>  {
> -	switch (info->state) {
> +	switch (nand->state) {
>  	case STATE_PIO_WRITING:
> -		__raw_writesl(info->mmio_base + NDDB, info->data_buff,
> -				DIV_ROUND_UP(info->data_size, 4));
> -		if (info->oob_size > 0)
> -			__raw_writesl(info->mmio_base + NDDB, info->oob_buff,
> -					DIV_ROUND_UP(info->oob_size, 4));
> +		__raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
> +				DIV_ROUND_UP(nand->data_size, 4));
> +		if (nand->oob_size > 0)
> +			__raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
> +					DIV_ROUND_UP(nand->oob_size, 4));
>  		break;
>  	case STATE_PIO_READING:
> -		__raw_readsl(info->mmio_base + NDDB, info->data_buff,
> -				DIV_ROUND_UP(info->data_size, 4));
> -		if (info->oob_size > 0)
> -			__raw_readsl(info->mmio_base + NDDB, info->oob_buff,
> -					DIV_ROUND_UP(info->oob_size, 4));
> +		__raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
> +				DIV_ROUND_UP(nand->data_size, 4));
> +		if (nand->oob_size > 0)
> +			__raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
> +					DIV_ROUND_UP(nand->oob_size, 4));
>  		break;
>  	default:
>  		printk(KERN_ERR "%s: invalid state %d\n", __func__,
> -				info->state);
> +				nand->state);
>  		BUG();
>  	}
>  }
>  
> -static void start_data_dma(struct pxa3xx_nand_info *info)
> +static void start_data_dma(struct pxa3xx_nand *nand)
>  {
> -	struct pxa_dma_desc *desc = info->data_desc;
> -	int dma_len = ALIGN(info->data_size + info->oob_size, 32);
> +	struct pxa_dma_desc *desc = nand->data_desc;
> +	int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
>  
>  	desc->ddadr = DDADR_STOP;
>  	desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
>  
> -	switch (info->state) {
> +	switch (nand->state) {
>  	case STATE_DMA_WRITING:
> -		desc->dsadr = info->data_buff_phys;
> -		desc->dtadr = info->mmio_phys + NDDB;
> +		desc->dsadr = nand->data_buff_phys;
> +		desc->dtadr = nand->mmio_phys + NDDB;
>  		desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
>  		break;
>  	case STATE_DMA_READING:
> -		desc->dtadr = info->data_buff_phys;
> -		desc->dsadr = info->mmio_phys + NDDB;
> +		desc->dtadr = nand->data_buff_phys;
> +		desc->dsadr = nand->mmio_phys + NDDB;
>  		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
>  		break;
>  	default:
>  		printk(KERN_ERR "%s: invalid state %d\n", __func__,
> -				info->state);
> +				nand->state);
>  		BUG();
>  	}
>  
> -	DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
> -	DDADR(info->data_dma_ch) = info->data_desc_addr;
> -	DCSR(info->data_dma_ch) |= DCSR_RUN;
> +	DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
> +	DDADR(nand->data_dma_ch) = nand->data_desc_addr;
> +	DCSR(nand->data_dma_ch) |= DCSR_RUN;
>  }
>  
>  static void pxa3xx_nand_data_dma_irq(int channel, void *data)
>  {
> -	struct pxa3xx_nand_info *info = data;
> +	struct pxa3xx_nand *nand = data;
>  	uint32_t dcsr;
>  
>  	dcsr = DCSR(channel);
>  	DCSR(channel) = dcsr;
>  
>  	if (dcsr & DCSR_BUSERR) {
> -		info->retcode = ERR_DMABUSERR;
> +		nand->retcode = ERR_DMABUSERR;
>  	}
>  
> -	info->state = STATE_DMA_DONE;
> -	enable_int(info, NDCR_INT_MASK);
> -	nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
> +	nand->state = STATE_DMA_DONE;
> +	enable_int(nand, NDCR_INT_MASK);
> +	nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>  }
>  
>  static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>  {
> -	struct pxa3xx_nand_info *info = devid;
> -	unsigned int status, is_completed = 0;
> +	struct pxa3xx_nand *nand = devid;
> +	struct pxa3xx_nand_info *info;
> +	unsigned int status, is_completed = 0, cs;
> +	unsigned int ready, cmd_done, page_done, badblock_detect;
>  
> -	status = nand_readl(info, NDSR);
> +	cs		= nand->chip_select;
> +	ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
> +	cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
> +	page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
> +	badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;

This is confusing... do you use to ?: operator for differentiating between
cs = 0 and cs = 1?
I think this is a bad idea.
Moreover, the use of ?: is discouraged among the kernel.

> +	info            = nand->info[cs];
>  
> +	status = nand_readl(nand, NDSR);
>  	if (status & NDSR_DBERR)
> -		info->retcode = ERR_DBERR;
> +		nand->retcode = ERR_DBERR;
>  	if (status & NDSR_SBERR)
> -		info->retcode = ERR_SBERR;
> +		nand->retcode = ERR_SBERR;
>  	if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
>  		/* whether use dma to transfer data */
> -		if (info->use_dma) {
> -			disable_int(info, NDCR_INT_MASK);
> -			info->state = (status & NDSR_RDDREQ) ?
> +		if (nand->use_dma) {
> +			disable_int(nand, NDCR_INT_MASK);
> +			nand->state = (status & NDSR_RDDREQ) ?
>  				      STATE_DMA_READING : STATE_DMA_WRITING;
> -			start_data_dma(info);
> +			start_data_dma(nand);
>  			goto NORMAL_IRQ_EXIT;
>  		} else {
> -			info->state = (status & NDSR_RDDREQ) ?
> +			nand->state = (status & NDSR_RDDREQ) ?
>  				      STATE_PIO_READING : STATE_PIO_WRITING;
> -			handle_data_pio(info);
> +			handle_data_pio(nand);
>  		}
>  	}
> -	if (status & NDSR_CS0_CMDD) {
> -		info->state = STATE_CMD_DONE;
> +	if (status & cmd_done) {
> +		nand->state = STATE_CMD_DONE;
>  		is_completed = 1;
>  	}
> -	if (status & NDSR_FLASH_RDY) {
> -		info->is_ready = 1;
> -		info->state = STATE_READY;
> +	if (status & ready) {
> +		nand->is_ready = 1;
> +		nand->state = STATE_READY;
>  	}
>  
>  	if (status & NDSR_WRCMDREQ) {
> -		nand_writel(info, NDSR, NDSR_WRCMDREQ);
> +		nand_writel(nand, NDSR, NDSR_WRCMDREQ);
>  		status &= ~NDSR_WRCMDREQ;
> -		info->state = STATE_CMD_HANDLE;
> -		nand_writel(info, NDCB0, info->ndcb0);
> -		nand_writel(info, NDCB0, info->ndcb1);
> -		nand_writel(info, NDCB0, info->ndcb2);
> +		nand->state = STATE_CMD_HANDLE;
> +		nand_writel(nand, NDCB0, nand->ndcb0);
> +		nand_writel(nand, NDCB0, nand->ndcb1);
> +		nand_writel(nand, NDCB0, nand->ndcb2);
>  	}
>  
>  	/* clear NDSR to let the controller exit the IRQ */
> -	nand_writel(info, NDSR, status);
> +	nand_writel(nand, NDSR, status);
>  	if (is_completed)
> -		complete(&info->cmd_complete);
> +		complete(&nand->cmd_complete);
>  NORMAL_IRQ_EXIT:
>  	return IRQ_HANDLED;
>  }
>  
> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
> -{
> -	struct pxa3xx_nand_info *info = mtd->priv;
> -	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
> -}
> -
>  static inline int is_buf_blank(uint8_t *buf, size_t len)
>  {
>  	for (; len > 0; len--)
> @@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
>  	return 1;
>  }
>  
> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>  		uint16_t column, int page_addr)
>  {
>  	uint16_t cmd;
>  	int addr_cycle, exec_cmd, ndcb0;
> -	struct mtd_info *mtd = info->mtd;
> +	struct mtd_info *mtd;
> +	struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>  
> -	ndcb0 = 0;
> +	mtd = get_mtd_by_info(info);
> +	ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;

This one is confusing too...
Besides, you don't need the parenthesis.

>  	addr_cycle = 0;
>  	exec_cmd = 1;
>  
>  	/* reset data and oob column point to handle data */
> -	info->buf_start		= 0;
> -	info->buf_count		= 0;
> -	info->oob_size		= 0;
> -	info->use_ecc		= 0;
> -	info->is_ready		= 0;
> -	info->retcode		= ERR_NONE;
> +	nand->buf_start		= 0;
> +	nand->buf_count		= 0;
> +	nand->oob_size		= 0;
> +	nand->use_ecc		= 0;
> +	nand->is_ready		= 0;
> +	nand->retcode		= ERR_NONE;
> +	nand->data_size		= 0;
> +	nand->use_dma		= 0;
> +	nand->command		= command;
>  
>  	switch (command) {
>  	case NAND_CMD_READ0:
>  	case NAND_CMD_PAGEPROG:
> -		info->use_ecc = 1;
> +		nand->use_ecc = 1;
>  	case NAND_CMD_READOOB:
>  		pxa3xx_set_datasize(info);
> +		nand->oob_buff = nand->data_buff + nand->data_size;
> +		nand->use_dma = use_dma;
>  		break;
>  	case NAND_CMD_SEQIN:
>  		exec_cmd = 0;
>  		break;
>  	default:
> -		info->ndcb1 = 0;
> -		info->ndcb2 = 0;
> +		nand->ndcb1 = 0;
> +		nand->ndcb2 = 0;
>  		break;
>  	}
>  
> -	info->ndcb0 = ndcb0;
> +	nand->ndcb0 = ndcb0;
>  	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
>  				    + info->col_addr_cycles);
>  
> @@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  	case NAND_CMD_READ0:
>  		cmd = info->cmdset->read1;
>  		if (command == NAND_CMD_READOOB)
> -			info->buf_start = mtd->writesize + column;
> +			nand->buf_start = mtd->writesize + column;
>  		else
> -			info->buf_start = column;
> +			nand->buf_start = column;
>  
>  		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
> -			info->ndcb0 |= NDCB0_CMD_TYPE(0)
> +			nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>  					| addr_cycle
>  					| (cmd & NDCB0_CMD1_MASK);
>  		else
> -			info->ndcb0 |= NDCB0_CMD_TYPE(0)
> +			nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>  					| NDCB0_DBC
>  					| addr_cycle
>  					| cmd;
> @@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  	case NAND_CMD_SEQIN:
>  		/* small page addr setting */
>  		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
> -			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
> +			nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>  					| (column & 0xFF);
>  
> -			info->ndcb2 = 0;
> +			nand->ndcb2 = 0;
>  		} else {
> -			info->ndcb1 = ((page_addr & 0xFFFF) << 16)
> +			nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
>  					| (column & 0xFFFF);
>  
>  			if (page_addr & 0xFF0000)
> -				info->ndcb2 = (page_addr & 0xFF0000) >> 16;
> +				nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
>  			else
> -				info->ndcb2 = 0;
> +				nand->ndcb2 = 0;
>  		}
>  
> -		info->buf_count = mtd->writesize + mtd->oobsize;
> -		memset(info->data_buff, 0xFF, info->buf_count);
> +		nand->buf_count = mtd->writesize + mtd->oobsize;
> +		memset(nand->data_buff, 0xFF, nand->buf_count);
>  
>  		break;
>  
>  	case NAND_CMD_PAGEPROG:
> -		if (is_buf_blank(info->data_buff,
> +		if (is_buf_blank(nand->data_buff,
>  					(mtd->writesize + mtd->oobsize))) {
>  			exec_cmd = 0;
>  			break;
>  		}
>  
>  		cmd = info->cmdset->program;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>  				| NDCB0_AUTO_RS
>  				| NDCB0_ST_ROW_EN
>  				| NDCB0_DBC
> @@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  
>  	case NAND_CMD_READID:
>  		cmd = info->cmdset->read_id;
> -		info->buf_count = info->read_id_bytes;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(3)
> +		nand->buf_count = info->read_id_bytes;
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(3)
>  				| NDCB0_ADDR_CYC(1)
>  				| cmd;
>  
> -		info->data_size = 8;
> +		nand->data_size = 8;
>  		break;
>  	case NAND_CMD_STATUS:
>  		cmd = info->cmdset->read_status;
> -		info->buf_count = 1;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(4)
> +		nand->buf_count = 1;
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(4)
>  				| NDCB0_ADDR_CYC(1)
>  				| cmd;
>  
> -		info->data_size = 8;
> +		nand->data_size = 8;
>  		break;
>  
>  	case NAND_CMD_ERASE1:
>  		cmd = info->cmdset->erase;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(2)
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(2)
>  				| NDCB0_AUTO_RS
>  				| NDCB0_ADDR_CYC(3)
>  				| NDCB0_DBC
>  				| cmd;
> -		info->ndcb1 = page_addr;
> -		info->ndcb2 = 0;
> +		nand->ndcb1 = page_addr;
> +		nand->ndcb2 = 0;
>  
>  		break;
>  	case NAND_CMD_RESET:
>  		cmd = info->cmdset->reset;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(5)
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(5)
>  				| cmd;
>  
>  		break;
> @@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>  				int column, int page_addr)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  	int ret, exec_cmd;
>  
>  	/*
> @@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>  	if (info->reg_ndcr & NDCR_DWIDTH_M)
>  		column /= 2;
>  
> -	exec_cmd = prepare_command_pool(info, command, column, page_addr);
> +	/*
> +	 * There may be different NAND chip hooked to
> +	 * different chip select, so check whether
> +	 * chip select has been changed, if yes, reset the timing
> +	 */
> +	if (nand->chip_select != info->chip_select) {
> +		nand->chip_select = info->chip_select;
> +		nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
> +		nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
> +	}
> +
> +	nand->state = STATE_PREPARED;
> +	exec_cmd = prepare_command_pool(nand, command, column, page_addr);
>  	if (exec_cmd) {
> -		init_completion(&info->cmd_complete);
> -		pxa3xx_nand_start(info);
> +		init_completion(&nand->cmd_complete);
> +		pxa3xx_nand_start(nand);
>  
> -		ret = wait_for_completion_timeout(&info->cmd_complete,
> +		ret = wait_for_completion_timeout(&nand->cmd_complete,
>  				CHIP_DELAY_TIMEOUT);
>  		if (!ret) {
>  			printk(KERN_ERR "Wait time out!!!\n");
>  			/* Stop State Machine for next command cycle */
> -			pxa3xx_nand_stop(info);
> +			pxa3xx_nand_stop(nand);
>  		}
> -		info->state = STATE_IDLE;
>  	}
> +	nand->state = STATE_IDLE;
>  }
>  
>  static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
> @@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>  		struct nand_chip *chip, uint8_t *buf, int page)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  
>  	chip->read_buf(mtd, buf, mtd->writesize);
>  	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>  
> -	if (info->retcode == ERR_SBERR) {
> +	if (nand->retcode == ERR_SBERR) {
>  		switch (info->use_ecc) {
>  		case 1:
>  			mtd->ecc_stats.corrected++;
> @@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>  		default:
>  			break;
>  		}
> -	} else if (info->retcode == ERR_DBERR) {
> +	} else if (nand->retcode == ERR_DBERR) {
>  		/*
>  		 * for blank page (all 0xff), HW will calculate its ECC as
>  		 * 0, which is different from the ECC information within
>  		 * OOB, ignore such double bit errors
>  		 */
>  		if (is_buf_blank(buf, mtd->writesize))
> -			info->retcode = ERR_NONE;
> +			nand->retcode = ERR_NONE;
>  		else
>  			mtd->ecc_stats.failed++;
>  	}
> @@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>  static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  	char retval = 0xFF;
>  
> -	if (info->buf_start < info->buf_count)
> +	if (nand->buf_start < nand->buf_count)
>  		/* Has just send a new command? */
> -		retval = info->data_buff[info->buf_start++];
> +		retval = nand->data_buff[nand->buf_start++];
>  
>  	return retval;
>  }
> @@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>  static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  	u16 retval = 0xFFFF;
>  
> -	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
> -		retval = *((u16 *)(info->data_buff+info->buf_start));
> -		info->buf_start += 2;
> +	if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
> +		retval = *((u16 *)(nand->data_buff+nand->buf_start));
> +		nand->buf_start += 2;
>  	}
>  	return retval;
>  }
> @@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>  static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> -	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>  
> -	memcpy(buf, info->data_buff + info->buf_start, real_len);
> -	info->buf_start += real_len;
> +	memcpy(buf, nand->data_buff + nand->buf_start, real_len);
> +	nand->buf_start += real_len;
>  }
>  
>  static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
>  		const uint8_t *buf, int len)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> -	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>  
> -	memcpy(info->data_buff + info->buf_start, buf, real_len);
> -	info->buf_start += real_len;
> +	memcpy(nand->data_buff + nand->buf_start, buf, real_len);
> +	nand->buf_start += real_len;
>  }
>  
>  static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
> @@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>  static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  
>  	/* pxa3xx_nand_send_command has waited for command complete */
>  	if (this->state == FL_WRITING || this->state == FL_ERASING) {
> -		if (info->retcode == ERR_NONE)
> +		if (nand->retcode == ERR_NONE)
>  			return 0;
>  		else {
>  			/*
> @@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>  static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>  				    const struct pxa3xx_nand_flash *f)
>  {
> -	struct platform_device *pdev = info->pdev;
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	struct platform_device *pdev = nand->pdev;
>  	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>  	uint32_t ndcr = 0x0; /* enable all interrupts */
>  
> @@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>  	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>  
>  	info->reg_ndcr = ndcr;
> +	info->use_ecc = 1;
>  
>  	pxa3xx_nand_set_timing(info, f->timing);
>  	return 0;
> @@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>  
>  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>  {
> -	uint32_t ndcr = nand_readl(info, NDCR);
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	uint32_t ndcr = nand_readl(nand, NDCR);
> +
> +	if (info->chip_select > 0) {
> +		printk(KERN_ERR "We could not detect configure"
> +				" if more than one cs is supported!!\n");
> +		BUG();

like Daniel already noticed, may be dev_err() is enough?

> +	}
>  	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
>  	/* set info fields needed to read id */
>  	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
>  	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
>  	info->cmdset = &default_cmdset;
>  
> -	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> -	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
> +	info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
> +	info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
>  
>  	return 0;
>  }
> @@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>   */
>  #define MAX_BUFF_SIZE	PAGE_SIZE
>  
> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
>  {
> -	struct platform_device *pdev = info->pdev;
> -	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
> -
> -	if (use_dma == 0) {
> -		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> -		if (info->data_buff == NULL)
> -			return -ENOMEM;
> -		return 0;
> -	}
> -
> -	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> -				&info->data_buff_phys, GFP_KERNEL);
> -	if (info->data_buff == NULL) {
> -		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> -		return -ENOMEM;
> -	}
> -
> -	info->data_buff_size = MAX_BUFF_SIZE;
> -	info->data_desc = (void *)info->data_buff + data_desc_offset;
> -	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
> +	struct pxa3xx_nand *nand;
> +	struct mtd_info *mtd;
>  
> -	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> -				pxa3xx_nand_data_dma_irq, info);
> -	if (info->data_dma_ch < 0) {
> -		dev_err(&pdev->dev, "failed to request data dma\n");
> -		dma_free_coherent(&pdev->dev, info->data_buff_size,
> -				info->data_buff, info->data_buff_phys);
> -		return info->data_dma_ch;
> -	}
> +	if (!info)
> +		return;
>  
> -	return 0;
> +	nand = info->nand_data;
> +	mtd = get_mtd_by_info(info);
> +	kfree(mtd);
> +	nand->info[cs] = NULL;
>  }
>  
>  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>  {
> -	struct mtd_info *mtd = info->mtd;
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	struct mtd_info *mtd = get_mtd_by_info(info);
>  	struct nand_chip *chip = mtd->priv;
>  
>  	/* use the common timing to make a try */
> -	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
> +	if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
> +		return 0;
>  	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
> -	if (info->is_ready)
> +	if (nand->is_ready)
>  		return 1;
>  	else
>  		return 0;

I think it is time to change this function return convention to propagate
errors and not just 0 or 1, (may be in separate patch) what do you think?

> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>  static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> -	struct platform_device *pdev = info->pdev;
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	struct platform_device *pdev = nand->pdev;
>  	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>  	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>  	const struct pxa3xx_nand_flash *f = NULL;
> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  	uint64_t chipsize;
>  	int i, ret, num;
>  
> +	nand->chip_select = info->chip_select;
>  	if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>  		goto KEEP_CONFIG;
>  
>  	ret = pxa3xx_nand_sensing(info);
>  	if (!ret) {
> -		kfree(mtd);
> -		info->mtd = NULL;
> -		printk(KERN_INFO "There is no nand chip on cs 0!\n");
> +		free_cs_resource(info, nand->chip_select);
> +		printk(KERN_INFO "There is no nand chip on cs %d!\n",
> +				nand->chip_select);
>  
>  		return -EINVAL;
>  	}
>  
>  	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
> -	id = *((uint16_t *)(info->data_buff));
> +	id = *((uint16_t *)(nand->data_buff));
>  	if (id != 0)
>  		printk(KERN_INFO "Detect a flash id %x\n", id);
>  	else {
> -		kfree(mtd);
> -		info->mtd = NULL;
> -		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");

Is this warning no longer needed?

> -
> +		free_cs_resource(info, nand->chip_select);
>  		return -EINVAL;
>  	}
>  
> @@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  	}
>  
>  	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
> -		kfree(mtd);
> -		info->mtd = NULL;
> +		free_cs_resource(info, nand->chip_select);
>  		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
>  
>  		return -EINVAL;
>  	}
>  
> -	pxa3xx_nand_config_flash(info, f);
> +	if (pxa3xx_nand_config_flash(info, f)) {
> +		printk(KERN_ERR "ERROR! Configure failed\n");
> +		return -EINVAL;
> +	}

Although, the pxa3xx_nand_config_flash() returns only 0 or -EINVAL,
it is better to propagate its return value.

>  	pxa3xx_flash_ids[0].name = f->name;
>  	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
>  	pxa3xx_flash_ids[0].pagesize = f->page_size;
> @@ -950,13 +977,13 @@ KEEP_CONFIG:
>  	if (nand_scan_ident(mtd, 1, def))
>  		return -ENODEV;
>  	/* calculate addressing information */
> +	nand->oob_buff = nand->data_buff + mtd->writesize;
>  	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
> -	info->oob_buff = info->data_buff + mtd->writesize;
>  	if ((mtd->size >> chip->page_shift) > 65536)
>  		info->row_addr_cycles = 3;
>  	else
>  		info->row_addr_cycles = 2;
> -	mtd->name = mtd_names[0];
> +	mtd->name = mtd_names[nand->chip_select];
>  	chip->ecc.mode = NAND_ECC_HW;
>  	chip->ecc.size = info->page_size;
>  
> @@ -967,51 +994,33 @@ KEEP_CONFIG:
>  	return nand_scan_tail(mtd);
>  }
>  
> -static
> -struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
> +static int alloc_nand_resource(struct platform_device *pdev)
>  {
> +	struct pxa3xx_nand_platform_data *pdata;
>  	struct pxa3xx_nand_info *info;
>  	struct nand_chip *chip;
>  	struct mtd_info *mtd;
> +	struct pxa3xx_nand *nand;
>  	struct resource *r;
> -	int ret, irq;
> +	int ret, irq, cs;
> +	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>  
> -	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
> -			GFP_KERNEL);
> -	if (!mtd) {
> +	pdata = pdev->dev.platform_data;
> +	nand = kzalloc(sizeof(struct mtd_info)
> +			+ sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
> +	if (!nand) {
>  		dev_err(&pdev->dev, "failed to allocate memory\n");
> -		return NULL;
> +		return -ENOMEM;
>  	}
>  
> -	info = (struct pxa3xx_nand_info *)(&mtd[1]);
> -	chip = (struct nand_chip *)(&mtd[1]);
> -	info->pdev = pdev;
> -	info->mtd = mtd;
> -	mtd->priv = info;
> -	mtd->owner = THIS_MODULE;
> -
> -	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
> -	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
> -	chip->controller        = &info->controller;
> -	chip->waitfunc		= pxa3xx_nand_waitfunc;
> -	chip->select_chip	= pxa3xx_nand_select_chip;
> -	chip->dev_ready		= pxa3xx_nand_dev_ready;
> -	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
> -	chip->read_word		= pxa3xx_nand_read_word;
> -	chip->read_byte		= pxa3xx_nand_read_byte;
> -	chip->read_buf		= pxa3xx_nand_read_buf;
> -	chip->write_buf		= pxa3xx_nand_write_buf;
> -	chip->verify_buf	= pxa3xx_nand_verify_buf;
> -
> -	spin_lock_init(&chip->controller->lock);
> -	init_waitqueue_head(&chip->controller->wq);
> -	info->clk = clk_get(&pdev->dev, NULL);
> -	if (IS_ERR(info->clk)) {
> +	nand->pdev = pdev;
> +	nand->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(nand->clk)) {
>  		dev_err(&pdev->dev, "failed to get nand clock\n");
> -		ret = PTR_ERR(info->clk);
> -		goto fail_free_mtd;
> +		ret = PTR_ERR(nand->clk);
> +		goto fail_alloc;
>  	}
> -	clk_enable(info->clk);
> +	clk_enable(nand->clk);
>  
>  	r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>  	if (r == NULL) {
> @@ -1019,7 +1028,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>  		ret = -ENXIO;
>  		goto fail_put_clk;
>  	}
> -	info->drcmr_dat = r->start;
> +	nand->drcmr_dat = r->start;
>  
>  	r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
>  	if (r == NULL) {
> @@ -1027,7 +1036,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>  		ret = -ENXIO;
>  		goto fail_put_clk;
>  	}
> -	info->drcmr_cmd = r->start;
> +	nand->drcmr_cmd = r->start;
>  
>  	irq = platform_get_irq(pdev, 0);
>  	if (irq < 0) {
> @@ -1050,81 +1059,147 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>  		goto fail_put_clk;
>  	}
>  
> -	info->mmio_base = ioremap(r->start, resource_size(r));
> -	if (info->mmio_base == NULL) {
> +	nand->mmio_base = ioremap(r->start, resource_size(r));
> +	if (nand->mmio_base == NULL) {
>  		dev_err(&pdev->dev, "ioremap() failed\n");
>  		ret = -ENODEV;
>  		goto fail_free_res;
>  	}
> -	info->mmio_phys = r->start;
> -
> -	ret = pxa3xx_nand_init_buff(info);
> -	if (ret)
> -		goto fail_free_io;
> +	nand->mmio_phys = r->start;
>  
>  	/* initialize all interrupts to be disabled */
> -	disable_int(info, NDSR_MASK);
> +	disable_int(nand, NDSR_MASK);
>  
>  	ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED,
> -			  pdev->name, info);
> +			  pdev->name, nand);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "failed to request IRQ\n");
> -		goto fail_free_buf;
> +		ret = -ENXIO;
> +		goto fail_free_io;
> +	}
> +
> +	platform_set_drvdata(pdev, nand);
> +
> +	spin_lock_init(&nand->controller.lock);
> +	init_waitqueue_head(&nand->controller.wq);
> +	for (cs = 0; cs < pdata->cs_num; cs++) {
> +		mtd = kzalloc(sizeof(struct mtd_info)
> +				+ sizeof(struct pxa3xx_nand_info),
> +				GFP_KERNEL);
> +		if (!mtd) {
> +			dev_err(&pdev->dev, "failed to allocate memory\n");
> +			ret = -ENOMEM;
> +			goto fail_free_irq;
> +		}
> +
> +		info = (struct pxa3xx_nand_info *)(&mtd[1]);
> +		info->nand_data = nand;
> +		info->chip_select = cs;
> +		mtd->priv = info;
> +		mtd->owner = THIS_MODULE;
> +		nand->info[cs] = info;
> +
> +		chip = (struct nand_chip *)(&mtd[1]);
> +		chip->controller        = &nand->controller;
> +		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
> +		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
> +		chip->waitfunc		= pxa3xx_nand_waitfunc;
> +		chip->select_chip	= pxa3xx_nand_select_chip;
> +		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
> +		chip->read_word		= pxa3xx_nand_read_word;
> +		chip->read_byte		= pxa3xx_nand_read_byte;
> +		chip->read_buf		= pxa3xx_nand_read_buf;
> +		chip->write_buf		= pxa3xx_nand_write_buf;
> +		chip->verify_buf	= pxa3xx_nand_verify_buf;
>  	}
>  
> -	platform_set_drvdata(pdev, info);
> +	if (use_dma == 0) {
> +		nand->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> +		if (nand->data_buff == NULL) {
> +			ret = -ENOMEM;
> +			goto fail_free_buf;
> +		}
> +		goto success_exit;
> +	}
>  
> -	return info;
> +	nand->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> +			&nand->data_buff_phys, GFP_KERNEL);
> +	if (nand->data_buff == NULL) {
> +		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> +		ret = -ENOMEM;
> +		goto fail_free_buf;
> +	}
>  
> +	nand->data_desc = (void *)nand->data_buff + data_desc_offset;
> +	nand->data_desc_addr = nand->data_buff_phys + data_desc_offset;
> +	nand->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> +			pxa3xx_nand_data_dma_irq, nand);
> +	if (nand->data_dma_ch < 0) {
> +		dev_err(&pdev->dev, "failed to request data dma\n");
> +		ret = -ENXIO;
> +		goto fail_free_dma_buf;
> +	}
> +success_exit:
> +	return 0;
> +
> +fail_free_dma_buf:
> +	dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
> +			nand->data_buff, nand->data_buff_phys);
>  fail_free_buf:
> -	free_irq(irq, info);
> -	if (use_dma) {
> -		pxa_free_dma(info->data_dma_ch);
> -		dma_free_coherent(&pdev->dev, info->data_buff_size,
> -			info->data_buff, info->data_buff_phys);
> -	} else
> -		kfree(info->data_buff);
> +	for (cs = 0; cs < pdata->cs_num; cs++) {
> +		info = nand->info[cs];
> +		free_cs_resource(info, cs);
> +	}
> +fail_free_irq:
> +	free_irq(irq, nand);
>  fail_free_io:
> -	iounmap(info->mmio_base);
> +	iounmap(nand->mmio_base);
>  fail_free_res:
>  	release_mem_region(r->start, resource_size(r));
>  fail_put_clk:
> -	clk_disable(info->clk);
> -	clk_put(info->clk);
> -fail_free_mtd:
> -	kfree(mtd);
> -	return NULL;
> +	clk_disable(nand->clk);
> +	clk_put(nand->clk);
> +fail_alloc:
> +	kfree(nand);
> +	return ret;
>  }
>  
>  static int pxa3xx_nand_remove(struct platform_device *pdev)
>  {
> -	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> -	struct mtd_info *mtd = info->mtd;
> +	struct pxa3xx_nand *nand = platform_get_drvdata(pdev);
> +	struct pxa3xx_nand_platform_data *pdata;
> +	struct pxa3xx_nand_info *info;
> +	struct mtd_info *mtd;
>  	struct resource *r;
> -	int irq;
> +	int irq, cs;
>  
>  	platform_set_drvdata(pdev, NULL);
> +	pdata = pdev->dev.platform_data;
>  
>  	irq = platform_get_irq(pdev, 0);
>  	if (irq >= 0)
> -		free_irq(irq, info);
> +		free_irq(irq, nand);
>  	if (use_dma) {
> -		pxa_free_dma(info->data_dma_ch);
> -		dma_free_writecombine(&pdev->dev, info->data_buff_size,
> -				info->data_buff, info->data_buff_phys);
> +		pxa_free_dma(nand->data_dma_ch);
> +		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
> +				nand->data_buff, nand->data_buff_phys);
>  	} else
> -		kfree(info->data_buff);
> +		kfree(nand->data_buff);
>  
> -	iounmap(info->mmio_base);
> +	iounmap(nand->mmio_base);
>  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	release_mem_region(r->start, resource_size(r));
>  
> -	clk_disable(info->clk);
> -	clk_put(info->clk);
> +	clk_disable(nand->clk);
> +	clk_put(nand->clk);
>  
> -	if (mtd) {
> +	for (cs = 0; cs < pdata->cs_num; cs++) {
> +		info = nand->info[cs];
> +		if (!info)
> +			continue;
> +		mtd = get_mtd_by_info(info);
>  		mtd_device_unregister(mtd);
> -		kfree(mtd);
> +		free_cs_resource(info, cs);
>  	}
>  	return 0;
>  }
> @@ -1134,34 +1209,54 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
>  	struct pxa3xx_nand_platform_data *pdata;
>  	struct pxa3xx_nand_info *info;
>  
> +	struct pxa3xx_nand *nand;
> +	struct mtd_info *mtd;
> +	int cs, ret, nr_parts, probe_success;
> +
> +	probe_success = 0;

Can this be done along with declaration?

>  	pdata = pdev->dev.platform_data;
>  	if (!pdata) {
>  		dev_err(&pdev->dev, "no platform data defined\n");
>  		return -ENODEV;
>  	}
>  
> -	info = alloc_nand_resource(pdev);
> -	if (info == NULL)
> +	ret = alloc_nand_resource(pdev);
> +	if (ret)
>  		return -ENOMEM;

Why not propagate the return value of alloc_nand_resource()?

>  
> -	if (pxa3xx_nand_scan(info->mtd)) {
> -		dev_err(&pdev->dev, "failed to scan nand\n");
> -		pxa3xx_nand_remove(pdev);
> -		return -ENODEV;
> -	}
> +	nand = platform_get_drvdata(pdev);
> +	for (cs = 0; cs < pdata->cs_num; cs++) {
> +		info = nand->info[cs];
> +		mtd = get_mtd_by_info(info);
> +		if (pxa3xx_nand_scan(mtd)) {
> +			dev_err(&pdev->dev, "failed to scan nand\n");

I think, it would be useful also to print here the return value of pxa3xx_nand_scan().

> +			continue;
> +		}
> +
> +		ret = 0;
> +		nr_parts = 0;
> +		if (mtd_has_cmdlinepart()) {
> +			const char *probes[] = { "cmdlinepart", NULL };
> +			struct mtd_partition *parts;
>  
> -	if (mtd_has_cmdlinepart()) {
> -		const char *probes[] = { "cmdlinepart", NULL };
> -		struct mtd_partition *parts;
> -		int nr_parts;
> +			nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0);
>  
> -		nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0);
> +			if (nr_parts)
> +				ret = mtd_device_register(mtd, parts, nr_parts);
> +		}
>  
> -		if (nr_parts)
> -			return mtd_device_register(info->mtd, parts, nr_parts);
> +		if (!nr_parts)
> +			ret = mtd_device_register(mtd, pdata->parts[cs],
> +					pdata->nr_parts[cs]);
> +		if (!ret)
> +			probe_success = 1;
>  	}
>  
> -	return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
> +	if (!probe_success) {
> +		pxa3xx_nand_remove(pdev);
> +		return -ENODEV;
> +	} else

You don't need this else statement

> +		return 0;
>  }
>  
>  #ifdef CONFIG_PM
> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>  	struct mtd_info *mtd = info->mtd;
>  
> -	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
> -	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
> +	nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
> +	nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>  	clk_enable(info->clk);
>  
>  	return 0;

I won't be able to test the patch in the near future, sorry...

-- 
Regards,
Igor.

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-22 13:06       ` Daniel Mack
@ 2011-06-23  6:22         ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-23  6:22 UTC (permalink / raw)
  To: Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

Hi Daniel,

On Wed, Jun 22, 2011 at 9:06 PM, Daniel Mack <zonque@gmail.com> wrote:
> Hi Lei,
>
> On Wed, Jun 22, 2011 at 2:21 PM, Lei Wen <adrian.wenl@gmail.com> wrote:
>> Hi Daniel,
>>
>> On Wed, Jun 22, 2011 at 7:39 PM, Daniel Mack <zonque@gmail.com> wrote:
>>> On Wed, Jun 22, 2011 at 5:17 AM, Lei Wen <leiwen@marvell.com> wrote:
>>>> Current pxa3xx_nand controller has two chip select which
>>>> both be workable. This patch enable this feature.
>>>>
>>>> Update platform driver to support this feature.
>>>>
>>>> Another 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.
>>>>
>>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>>> ---
>>>>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>>>>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>>>>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>>>>  arch/arm/mach-pxa/littleton.c                |    5 +-
>>>>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>>>>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>>>>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>>>>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>>>>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>>>>  9 files changed, 444 insertions(+), 338 deletions(-)
>>>
>>> This patch doesn't apply for me on top of Linus' master branch. Which
>>> tree are you based on currently?
>>
>> Actually it is based on Linux 3.0-rc2 and add two patches pending to
>> l2-mtd-2.6.git already:
>>
>> mtd: pxa3xx_nand: fix nand detection issue
>> mtd: pxa3xx_nand: Fix blank page ECC mismatch
>
> I have a similar setup, also with those two patches on top, but
> applying your patch gives me:
>
> Applying: MTD: pxa3xx_nand: enable multiple chip select support
> error: patch failed: drivers/mtd/nand/pxa3xx_nand.c:477
> error: drivers/mtd/nand/pxa3xx_nand.c: patch does not apply
> Patch failed at 0001 MTD: pxa3xx_nand: enable multiple chip select support
> When you have resolved this problem run "git am --resolved".
> If you would prefer to skip this patch, instead run "git am --skip".
> To restore the original branch and stop patching run "git am --abort".
>
> Could you push your branch somewhere?
>
>>>>  #ifdef CONFIG_PM
>>>> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>>        struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>>        struct mtd_info *mtd = info->mtd;
>>>>
>>>> -       nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>>>> -       nand_writel(info, NDTR1CS0, info->ndtr1cs0);
>>>> +       nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>>>> +       nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>>>>        clk_enable(info->clk);
>>>
>>> This won't compile.
>>
>> Could you post your build log?
>
> I didn't compile it because I couldn't, but "nand" is certainly
> undefined in this context now. Did you compile and test this code with
> "CONFIG_PM=y"?

You're right, I haven't take open PM config...
I would rebase this patch over l2-mtd-2.6.git for next post.

Thanks,
Lei

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-23  6:22         ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-23  6:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Daniel,

On Wed, Jun 22, 2011 at 9:06 PM, Daniel Mack <zonque@gmail.com> wrote:
> Hi Lei,
>
> On Wed, Jun 22, 2011 at 2:21 PM, Lei Wen <adrian.wenl@gmail.com> wrote:
>> Hi Daniel,
>>
>> On Wed, Jun 22, 2011 at 7:39 PM, Daniel Mack <zonque@gmail.com> wrote:
>>> On Wed, Jun 22, 2011 at 5:17 AM, Lei Wen <leiwen@marvell.com> wrote:
>>>> Current pxa3xx_nand controller has two chip select which
>>>> both be workable. This patch enable this feature.
>>>>
>>>> Update platform driver to support this feature.
>>>>
>>>> Another 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.
>>>>
>>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>>> ---
>>>> ?arch/arm/mach-mmp/aspenite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>>>> ?arch/arm/mach-pxa/cm-x300.c ? ? ? ? ? ? ? ? ?| ? ?5 +-
>>>> ?arch/arm/mach-pxa/colibri-pxa3xx.c ? ? ? ? ? | ? ?5 +-
>>>> ?arch/arm/mach-pxa/littleton.c ? ? ? ? ? ? ? ?| ? ?5 +-
>>>> ?arch/arm/mach-pxa/mxm8x10.c ? ? ? ? ? ? ? ? ?| ? ?9 +-
>>>> ?arch/arm/mach-pxa/raumfeld.c ? ? ? ? ? ? ? ? | ? ?5 +-
>>>> ?arch/arm/mach-pxa/zylonite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>>>> ?arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | ? ?8 +-
>>>> ?drivers/mtd/nand/pxa3xx_nand.c ? ? ? ? ? ? ? | ?735 +++++++++++++++-----------
>>>> ?9 files changed, 444 insertions(+), 338 deletions(-)
>>>
>>> This patch doesn't apply for me on top of Linus' master branch. Which
>>> tree are you based on currently?
>>
>> Actually it is based on Linux 3.0-rc2 and add two patches pending to
>> l2-mtd-2.6.git already:
>>
>> mtd: pxa3xx_nand: fix nand detection issue
>> mtd: pxa3xx_nand: Fix blank page ECC mismatch
>
> I have a similar setup, also with those two patches on top, but
> applying your patch gives me:
>
> Applying: MTD: pxa3xx_nand: enable multiple chip select support
> error: patch failed: drivers/mtd/nand/pxa3xx_nand.c:477
> error: drivers/mtd/nand/pxa3xx_nand.c: patch does not apply
> Patch failed at 0001 MTD: pxa3xx_nand: enable multiple chip select support
> When you have resolved this problem run "git am --resolved".
> If you would prefer to skip this patch, instead run "git am --skip".
> To restore the original branch and stop patching run "git am --abort".
>
> Could you push your branch somewhere?
>
>>>> ?#ifdef CONFIG_PM
>>>> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>> ? ? ? ?struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>> ? ? ? ?struct mtd_info *mtd = info->mtd;
>>>>
>>>> - ? ? ? nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>>>> - ? ? ? nand_writel(info, NDTR1CS0, info->ndtr1cs0);
>>>> + ? ? ? nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>>>> + ? ? ? nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>>>> ? ? ? ?clk_enable(info->clk);
>>>
>>> This won't compile.
>>
>> Could you post your build log?
>
> I didn't compile it because I couldn't, but "nand" is certainly
> undefined in this context now. Did you compile and test this code with
> "CONFIG_PM=y"?

You're right, I haven't take open PM config...
I would rebase this patch over l2-mtd-2.6.git for next post.

Thanks,
Lei

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-08 16:09   ` Igor Grinberg
@ 2011-07-08 15:26     ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-23  6:35 UTC (permalink / raw)
  To: Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

Hi Igor,

On Wed, Jun 22, 2011 at 9:45 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> Hi Lei,
>
>
> Some comments from a quick look...
>
>
> On 06/22/11 06:17, Lei Wen wrote:
>
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>
> You should _also_ put this comment inside the pxa3xx_nand.h
> may be even inside the pxa3xx_nand_platform_data structure,
> so people would not have to search the git log to find this problem.

How about just throw out a BUG() to force people cannot set keep_config
when supported cs num more than 1? Also appended with the comments above?

>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>>  arch/arm/mach-pxa/littleton.c                |    5 +-
>>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>>  9 files changed, 444 insertions(+), 338 deletions(-)
>>
>> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
>> index 06b5fa8..b6589d6 100644
>> --- a/arch/arm/mach-mmp/aspenite.c
>> +++ b/arch/arm/mach-mmp/aspenite.c
>> @@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
>>
>>  static struct pxa3xx_nand_platform_data aspenite_nand_info = {
>>       .enable_arbiter = 1,
>> -     .parts          = aspenite_nand_partitions,
>> -     .nr_parts       = ARRAY_SIZE(aspenite_nand_partitions),
>> +     .cs_num = 1,
>> +     .parts[0]       = aspenite_nand_partitions,
>> +     .nr_parts[0]    = ARRAY_SIZE(aspenite_nand_partitions),
>>  };
>>
>>  static struct i2c_board_info aspenite_i2c_info[] __initdata = {
>> diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
>> index b2248e7..d67eb7b 100644
>> --- a/arch/arm/mach-pxa/cm-x300.c
>> +++ b/arch/arm/mach-pxa/cm-x300.c
>> @@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
>>  static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
>>       .enable_arbiter = 1,
>>       .keep_config    = 1,
>> -     .parts          = cm_x300_nand_partitions,
>> -     .nr_parts       = ARRAY_SIZE(cm_x300_nand_partitions),
>> +     .cs_num         = 1,
>> +     .parts[0]       = cm_x300_nand_partitions,
>> +     .nr_parts[0]    = ARRAY_SIZE(cm_x300_nand_partitions),
>>  };
>>
>>  static void __init cm_x300_init_nand(void)
>> diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
>> index 3f9be41..ff7a07b 100644
>> --- a/arch/arm/mach-pxa/colibri-pxa3xx.c
>> +++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
>> @@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
>>  static struct pxa3xx_nand_platform_data colibri_nand_info = {
>>       .enable_arbiter = 1,
>>       .keep_config    = 1,
>> -     .parts          = colibri_nand_partitions,
>> -     .nr_parts       = ARRAY_SIZE(colibri_nand_partitions),
>> +     .cs_num         = 1,
>> +     .parts[0]       = colibri_nand_partitions,
>> +     .nr_parts[0]    = ARRAY_SIZE(colibri_nand_partitions),
>>  };
>>
>>  void __init colibri_pxa3xx_init_nand(void)
>> diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
>> index e5e326d..6eaf852 100644
>> --- a/arch/arm/mach-pxa/littleton.c
>> +++ b/arch/arm/mach-pxa/littleton.c
>> @@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
>>
>>  static struct pxa3xx_nand_platform_data littleton_nand_info = {
>>       .enable_arbiter = 1,
>> -     .parts          = littleton_nand_partitions,
>> -     .nr_parts       = ARRAY_SIZE(littleton_nand_partitions),
>> +     .cs_num         = 1,
>> +     .parts[0]       = littleton_nand_partitions,
>> +     .nr_parts[0]    = ARRAY_SIZE(littleton_nand_partitions),
>>  };
>>
>>  static void __init littleton_init_nand(void)
>> diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
>> index b5a8fd3..e7ce135 100644
>> --- a/arch/arm/mach-pxa/mxm8x10.c
>> +++ b/arch/arm/mach-pxa/mxm8x10.c
>> @@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
>>  };
>>
>>  static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
>> -     .enable_arbiter = 1,
>> -     .keep_config = 1,
>> -     .parts = mxm_8x10_nand_partitions,
>> -     .nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
>> +     .enable_arbiter = 1,
>> +     .keep_config    = 1,
>> +     .cs_num         = 1,
>> +     .parts[0]       = mxm_8x10_nand_partitions,
>> +     .nr_parts[0]    = ARRAY_SIZE(mxm_8x10_nand_partitions)
>>  };
>>
>>  static void __init mxm_8x10_nand_init(void)
>> diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
>> index d130f77..a54846d 100644
>> --- a/arch/arm/mach-pxa/raumfeld.c
>> +++ b/arch/arm/mach-pxa/raumfeld.c
>> @@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
>>  static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
>>       .enable_arbiter = 1,
>>       .keep_config    = 1,
>> -     .parts          = raumfeld_nand_partitions,
>> -     .nr_parts       = ARRAY_SIZE(raumfeld_nand_partitions),
>> +     .cs_num         = 1,
>> +     .parts[0]       = raumfeld_nand_partitions,
>> +     .nr_parts[0]    = ARRAY_SIZE(raumfeld_nand_partitions),
>>  };
>>
>>  /**
>> diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
>> index 5821185..ea4752a 100644
>> --- a/arch/arm/mach-pxa/zylonite.c
>> +++ b/arch/arm/mach-pxa/zylonite.c
>> @@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
>>
>>  static struct pxa3xx_nand_platform_data zylonite_nand_info = {
>>       .enable_arbiter = 1,
>> -     .parts          = zylonite_nand_partitions,
>> -     .nr_parts       = ARRAY_SIZE(zylonite_nand_partitions),
>> +     .cs_num         = 1,
>> +     .parts[0]       = zylonite_nand_partitions,
>> +     .nr_parts[0]    = ARRAY_SIZE(zylonite_nand_partitions),
>>  };
>>
>>  static void __init zylonite_init_nand(void)
>> diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>> index 442301f..34a3f52 100644
>> --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>> +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>> @@ -41,6 +41,8 @@ struct pxa3xx_nand_flash {
>>       struct pxa3xx_nand_timing *timing;      /* NAND Flash timing */
>>  };
>>
>> +/* The max num of chip select current support */
>
> /* The maximum number of chip selects currently supported */
>
>> +#define NUM_CHIP_SELECT              (2)
>>  struct pxa3xx_nand_platform_data {
>>
>>       /* the data flash bus is shared between the Static Memory
>> @@ -52,8 +54,10 @@ struct pxa3xx_nand_platform_data {
>>       /* allow platform code to keep OBM/bootloader defined NFC config */
>>       int     keep_config;
>>
>> -     const struct mtd_partition              *parts;
>> -     unsigned int                            nr_parts;
>> +     /* indicate how many chip select would be used for this platform */
>
> /* indicate how many chip selects will be used */
>
>> +     int     cs_num;
>
> This name is too confusing, I think even num_cs is better or cs_count?
> Also, may be align it with the others?

 I prefer the num_cs.

>
>> +     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;
>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
>> index 30689cc..259b8d5 100644
>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>> +++ b/drivers/mtd/nand/pxa3xx_nand.c
>> @@ -92,11 +92,13 @@
>>  #define NDCB0_ADDR_CYC_SHIFT (16)
>>
>>  /* macros for registers read/write */
>> -#define nand_writel(info, off, val)  \
>> -     __raw_writel((val), (info)->mmio_base + (off))
>> +#define nand_writel(nand, off, val)  \
>> +     __raw_writel((val), (nand)->mmio_base + (off))
>>
>> -#define nand_readl(info, off)                \
>> -     __raw_readl((info)->mmio_base + (off))
>> +#define nand_readl(nand, off)                \
>> +     __raw_readl((nand)->mmio_base + (off))
>> +#define get_mtd_by_info(info)                \
>> +     (struct mtd_info *)((void *)info - sizeof(struct mtd_info))
>>
>>  /* error code and state */
>>  enum {
>> @@ -110,6 +112,7 @@ enum {
>>
>>  enum {
>>       STATE_IDLE = 0,
>> +     STATE_PREPARED,
>>       STATE_CMD_HANDLE,
>>       STATE_DMA_READING,
>>       STATE_DMA_WRITING,
>> @@ -123,63 +126,63 @@ enum {
>>  struct pxa3xx_nand_info {
>>       struct nand_chip        nand_chip;
>>
>> -     struct nand_hw_control  controller;
>> -     struct platform_device   *pdev;
>>       struct pxa3xx_nand_cmdset *cmdset;
>> +     /* page size of attached chip */
>> +     uint16_t                page_size;
>> +     uint8_t                 chip_select;
>> +     uint8_t                 use_ecc;
>> +
>> +     /* calculated from pxa3xx_nand_flash data */
>> +     uint8_t                 col_addr_cycles;
>> +     uint8_t                 row_addr_cycles;
>> +     uint8_t                 read_id_bytes;
>> +
>> +     /* cached register value */
>> +     uint32_t                reg_ndcr;
>> +     uint32_t                ndtr0cs0;
>> +     uint32_t                ndtr1cs0;
>>
>> +     void                    *nand_data;
>> +};
>> +
>> +struct pxa3xx_nand {
>>       struct clk              *clk;
>>       void __iomem            *mmio_base;
>>       unsigned long           mmio_phys;
>> +     struct nand_hw_control  controller;
>> +     struct completion       cmd_complete;
>> +     struct platform_device   *pdev;
>
> please, align

Do you still mean cs_num? What this comment refer to?

>
>>
>> -     unsigned int            buf_start;
>> -     unsigned int            buf_count;
>> -
>> -     struct mtd_info         *mtd;
>>       /* DMA information */
>>       int                     drcmr_dat;
>>       int                     drcmr_cmd;
>> -
>> -     unsigned char           *data_buff;
>> -     unsigned char           *oob_buff;
>> -     dma_addr_t              data_buff_phys;
>> -     size_t                  data_buff_size;
>>       int                     data_dma_ch;
>> -     struct pxa_dma_desc     *data_desc;
>> +     dma_addr_t              data_buff_phys;
>>       dma_addr_t              data_desc_addr;
>> +     struct pxa_dma_desc     *data_desc;
>>
>> -     uint32_t                reg_ndcr;
>> -
>> -     /* saved column/page_addr during CMD_SEQIN */
>> -     int                     seqin_column;
>> -     int                     seqin_page_addr;
>> +     struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
>> +     uint32_t                command;
>> +     uint16_t                data_size;      /* data size in FIFO */
>> +     uint16_t                oob_size;
>> +     unsigned char           *data_buff;
>> +     unsigned char           *oob_buff;
>> +     uint32_t                buf_start;
>> +     uint32_t                buf_count;
>>
>>       /* relate to the command */
>>       unsigned int            state;
>> -
>> +     uint8_t                 chip_select;
>>       int                     use_ecc;        /* use HW ECC ? */
>>       int                     use_dma;        /* use DMA ? */
>>       int                     is_ready;
>> -
>> -     unsigned int            page_size;      /* page size of attached chip */
>> -     unsigned int            data_size;      /* data size in FIFO */
>>       int                     retcode;
>> -     struct completion       cmd_complete;
>>
>>       /* generated NDCBx register values */
>> +     uint8_t                 total_cmds;
>>       uint32_t                ndcb0;
>>       uint32_t                ndcb1;
>>       uint32_t                ndcb2;
>> -
>> -     /* timing calcuted from setting */
>> -     uint32_t                ndtr0cs0;
>> -     uint32_t                ndtr1cs0;
>> -
>> -     /* calculated from pxa3xx_nand_flash data */
>> -     size_t          oob_size;
>> -     size_t          read_id_bytes;
>> -
>> -     unsigned int    col_addr_cycles;
>> -     unsigned int    row_addr_cycles;
>>  };
>
> It looks like if you switch the names of the structures above,
> then the patch will be much shorter and cleaner,
> but will it make structures meaning confusion?

Could you elaborate more about this? Do you means no need to create a seperated
structure?

>
>>
>>  static int use_dma = 1;
>> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>>  /* Define a default flash type setting serve as flash detecting only */
>>  #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>>
>> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>>
>>  #define NDTR0_tCH(c) (min((c), 7) << 19)
>>  #define NDTR0_tCS(c) (min((c), 7) << 16)
>> @@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>>  static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>>                                  const struct pxa3xx_nand_timing *t)
>>  {
>> -     unsigned long nand_clk = clk_get_rate(info->clk);
>> +     struct pxa3xx_nand *nand = info->nand_data;
>> +     unsigned long nand_clk;
>>       uint32_t ndtr0, ndtr1;
>>
>> +     nand_clk = clk_get_rate(nand->clk);
>>       ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
>>               NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
>>               NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
>> @@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>>
>>       info->ndtr0cs0 = ndtr0;
>>       info->ndtr1cs0 = ndtr1;
>> -     nand_writel(info, NDTR0CS0, ndtr0);
>> -     nand_writel(info, NDTR1CS0, ndtr1);
>> +     nand_writel(nand, NDTR0CS0, ndtr0);
>> +     nand_writel(nand, NDTR1CS0, ndtr1);
>>  }
>>
>>  static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>>  {
>> +     struct pxa3xx_nand *nand = info->nand_data;
>>       int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
>>
>> -     info->data_size = info->page_size;
>> +     nand->data_size = info->page_size;
>>       if (!oob_enable) {
>> -             info->oob_size = 0;
>> +             nand->oob_size = 0;
>>               return;
>>       }
>>
>>       switch (info->page_size) {
>>       case 2048:
>> -             info->oob_size = (info->use_ecc) ? 40 : 64;
>> +             nand->oob_size = (info->use_ecc) ? 40 : 64;
>>               break;
>>       case 512:
>> -             info->oob_size = (info->use_ecc) ? 8 : 16;
>> +             nand->oob_size = (info->use_ecc) ? 8 : 16;
>>               break;
>>       }
>>  }
>> @@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>>   * 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_info *info)
>> +static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
>>  {
>> +     struct pxa3xx_nand_info *info;
>>       uint32_t ndcr;
>>
>> +     info = nand->info[nand->chip_select];
>>       ndcr = info->reg_ndcr;
>> -     ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
>> -     ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
>> +     ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
>> +     ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
>>       ndcr |= NDCR_ND_RUN;
>>
>>       /* clear status bits and run */
>> -     nand_writel(info, NDCR, 0);
>> -     nand_writel(info, NDSR, NDSR_MASK);
>> -     nand_writel(info, NDCR, ndcr);
>> +     nand_writel(nand, NDCR, 0);
>> +     nand_writel(nand, NDSR, NDSR_MASK);
>> +     nand_writel(nand, NDCR, ndcr);
>>  }
>>
>> -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
>> +static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
>>  {
>>       uint32_t ndcr;
>>       int timeout = NAND_STOP_DELAY;
>>
>>       /* wait RUN bit in NDCR become 0 */
>> -     ndcr = nand_readl(info, NDCR);
>> +     ndcr = nand_readl(nand, NDCR);
>>       while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
>> -             ndcr = nand_readl(info, NDCR);
>> +             ndcr = nand_readl(nand, NDCR);
>>               udelay(1);
>>       }
>>
>>       if (timeout <= 0) {
>>               ndcr &= ~NDCR_ND_RUN;
>> -             nand_writel(info, NDCR, ndcr);
>> +             nand_writel(nand, NDCR, ndcr);
>>       }
>>       /* clear status bits */
>> -     nand_writel(info, NDSR, NDSR_MASK);
>> +     nand_writel(nand, NDSR, NDSR_MASK);
>>  }
>>
>> -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>> +static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>>  {
>>       uint32_t ndcr;
>>
>> -     ndcr = nand_readl(info, NDCR);
>> -     nand_writel(info, NDCR, ndcr & ~int_mask);
>> +     ndcr = nand_readl(nand, NDCR);
>> +     nand_writel(nand, NDCR, ndcr & ~int_mask);
>>  }
>>
>> -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>> +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>>  {
>>       uint32_t ndcr;
>>
>> -     ndcr = nand_readl(info, NDCR);
>> -     nand_writel(info, NDCR, ndcr | int_mask);
>> +     ndcr = nand_readl(nand, NDCR);
>> +     nand_writel(nand, NDCR, ndcr | int_mask);
>>  }
>>
>> -static void handle_data_pio(struct pxa3xx_nand_info *info)
>> +static void handle_data_pio(struct pxa3xx_nand *nand)
>>  {
>> -     switch (info->state) {
>> +     switch (nand->state) {
>>       case STATE_PIO_WRITING:
>> -             __raw_writesl(info->mmio_base + NDDB, info->data_buff,
>> -                             DIV_ROUND_UP(info->data_size, 4));
>> -             if (info->oob_size > 0)
>> -                     __raw_writesl(info->mmio_base + NDDB, info->oob_buff,
>> -                                     DIV_ROUND_UP(info->oob_size, 4));
>> +             __raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
>> +                             DIV_ROUND_UP(nand->data_size, 4));
>> +             if (nand->oob_size > 0)
>> +                     __raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
>> +                                     DIV_ROUND_UP(nand->oob_size, 4));
>>               break;
>>       case STATE_PIO_READING:
>> -             __raw_readsl(info->mmio_base + NDDB, info->data_buff,
>> -                             DIV_ROUND_UP(info->data_size, 4));
>> -             if (info->oob_size > 0)
>> -                     __raw_readsl(info->mmio_base + NDDB, info->oob_buff,
>> -                                     DIV_ROUND_UP(info->oob_size, 4));
>> +             __raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
>> +                             DIV_ROUND_UP(nand->data_size, 4));
>> +             if (nand->oob_size > 0)
>> +                     __raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
>> +                                     DIV_ROUND_UP(nand->oob_size, 4));
>>               break;
>>       default:
>>               printk(KERN_ERR "%s: invalid state %d\n", __func__,
>> -                             info->state);
>> +                             nand->state);
>>               BUG();
>>       }
>>  }
>>
>> -static void start_data_dma(struct pxa3xx_nand_info *info)
>> +static void start_data_dma(struct pxa3xx_nand *nand)
>>  {
>> -     struct pxa_dma_desc *desc = info->data_desc;
>> -     int dma_len = ALIGN(info->data_size + info->oob_size, 32);
>> +     struct pxa_dma_desc *desc = nand->data_desc;
>> +     int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
>>
>>       desc->ddadr = DDADR_STOP;
>>       desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
>>
>> -     switch (info->state) {
>> +     switch (nand->state) {
>>       case STATE_DMA_WRITING:
>> -             desc->dsadr = info->data_buff_phys;
>> -             desc->dtadr = info->mmio_phys + NDDB;
>> +             desc->dsadr = nand->data_buff_phys;
>> +             desc->dtadr = nand->mmio_phys + NDDB;
>>               desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
>>               break;
>>       case STATE_DMA_READING:
>> -             desc->dtadr = info->data_buff_phys;
>> -             desc->dsadr = info->mmio_phys + NDDB;
>> +             desc->dtadr = nand->data_buff_phys;
>> +             desc->dsadr = nand->mmio_phys + NDDB;
>>               desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
>>               break;
>>       default:
>>               printk(KERN_ERR "%s: invalid state %d\n", __func__,
>> -                             info->state);
>> +                             nand->state);
>>               BUG();
>>       }
>>
>> -     DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
>> -     DDADR(info->data_dma_ch) = info->data_desc_addr;
>> -     DCSR(info->data_dma_ch) |= DCSR_RUN;
>> +     DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
>> +     DDADR(nand->data_dma_ch) = nand->data_desc_addr;
>> +     DCSR(nand->data_dma_ch) |= DCSR_RUN;
>>  }
>>
>>  static void pxa3xx_nand_data_dma_irq(int channel, void *data)
>>  {
>> -     struct pxa3xx_nand_info *info = data;
>> +     struct pxa3xx_nand *nand = data;
>>       uint32_t dcsr;
>>
>>       dcsr = DCSR(channel);
>>       DCSR(channel) = dcsr;
>>
>>       if (dcsr & DCSR_BUSERR) {
>> -             info->retcode = ERR_DMABUSERR;
>> +             nand->retcode = ERR_DMABUSERR;
>>       }
>>
>> -     info->state = STATE_DMA_DONE;
>> -     enable_int(info, NDCR_INT_MASK);
>> -     nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>> +     nand->state = STATE_DMA_DONE;
>> +     enable_int(nand, NDCR_INT_MASK);
>> +     nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>>  }
>>
>>  static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>>  {
>> -     struct pxa3xx_nand_info *info = devid;
>> -     unsigned int status, is_completed = 0;
>> +     struct pxa3xx_nand *nand = devid;
>> +     struct pxa3xx_nand_info *info;
>> +     unsigned int status, is_completed = 0, cs;
>> +     unsigned int ready, cmd_done, page_done, badblock_detect;
>>
>> -     status = nand_readl(info, NDSR);
>> +     cs              = nand->chip_select;
>> +     ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
>> +     cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
>> +     page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
>> +     badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
>
> This is confusing... do you use to ?: operator for differentiating between
> cs = 0 and cs = 1?
> I think this is a bad idea.
> Moreover, the use of ?: is discouraged among the kernel.

I could use if/else to replace this statement.

>
>> +     info            = nand->info[cs];
>>
>> +     status = nand_readl(nand, NDSR);
>>       if (status & NDSR_DBERR)
>> -             info->retcode = ERR_DBERR;
>> +             nand->retcode = ERR_DBERR;
>>       if (status & NDSR_SBERR)
>> -             info->retcode = ERR_SBERR;
>> +             nand->retcode = ERR_SBERR;
>>       if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
>>               /* whether use dma to transfer data */
>> -             if (info->use_dma) {
>> -                     disable_int(info, NDCR_INT_MASK);
>> -                     info->state = (status & NDSR_RDDREQ) ?
>> +             if (nand->use_dma) {
>> +                     disable_int(nand, NDCR_INT_MASK);
>> +                     nand->state = (status & NDSR_RDDREQ) ?
>>                                     STATE_DMA_READING : STATE_DMA_WRITING;
>> -                     start_data_dma(info);
>> +                     start_data_dma(nand);
>>                       goto NORMAL_IRQ_EXIT;
>>               } else {
>> -                     info->state = (status & NDSR_RDDREQ) ?
>> +                     nand->state = (status & NDSR_RDDREQ) ?
>>                                     STATE_PIO_READING : STATE_PIO_WRITING;
>> -                     handle_data_pio(info);
>> +                     handle_data_pio(nand);
>>               }
>>       }
>> -     if (status & NDSR_CS0_CMDD) {
>> -             info->state = STATE_CMD_DONE;
>> +     if (status & cmd_done) {
>> +             nand->state = STATE_CMD_DONE;
>>               is_completed = 1;
>>       }
>> -     if (status & NDSR_FLASH_RDY) {
>> -             info->is_ready = 1;
>> -             info->state = STATE_READY;
>> +     if (status & ready) {
>> +             nand->is_ready = 1;
>> +             nand->state = STATE_READY;
>>       }
>>
>>       if (status & NDSR_WRCMDREQ) {
>> -             nand_writel(info, NDSR, NDSR_WRCMDREQ);
>> +             nand_writel(nand, NDSR, NDSR_WRCMDREQ);
>>               status &= ~NDSR_WRCMDREQ;
>> -             info->state = STATE_CMD_HANDLE;
>> -             nand_writel(info, NDCB0, info->ndcb0);
>> -             nand_writel(info, NDCB0, info->ndcb1);
>> -             nand_writel(info, NDCB0, info->ndcb2);
>> +             nand->state = STATE_CMD_HANDLE;
>> +             nand_writel(nand, NDCB0, nand->ndcb0);
>> +             nand_writel(nand, NDCB0, nand->ndcb1);
>> +             nand_writel(nand, NDCB0, nand->ndcb2);
>>       }
>>
>>       /* clear NDSR to let the controller exit the IRQ */
>> -     nand_writel(info, NDSR, status);
>> +     nand_writel(nand, NDSR, status);
>>       if (is_completed)
>> -             complete(&info->cmd_complete);
>> +             complete(&nand->cmd_complete);
>>  NORMAL_IRQ_EXIT:
>>       return IRQ_HANDLED;
>>  }
>>
>> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
>> -{
>> -     struct pxa3xx_nand_info *info = mtd->priv;
>> -     return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
>> -}
>> -
>>  static inline int is_buf_blank(uint8_t *buf, size_t len)
>>  {
>>       for (; len > 0; len--)
>> @@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
>>       return 1;
>>  }
>>
>> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>>               uint16_t column, int page_addr)
>>  {
>>       uint16_t cmd;
>>       int addr_cycle, exec_cmd, ndcb0;
>> -     struct mtd_info *mtd = info->mtd;
>> +     struct mtd_info *mtd;
>> +     struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>>
>> -     ndcb0 = 0;
>> +     mtd = get_mtd_by_info(info);
>> +     ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
>
> This one is confusing too...
> Besides, you don't need the parenthesis.

Got that. I would fix it.

>
>>       addr_cycle = 0;
>>       exec_cmd = 1;
>>
>>       /* reset data and oob column point to handle data */
>> -     info->buf_start         = 0;
>> -     info->buf_count         = 0;
>> -     info->oob_size          = 0;
>> -     info->use_ecc           = 0;
>> -     info->is_ready          = 0;
>> -     info->retcode           = ERR_NONE;
>> +     nand->buf_start         = 0;
>> +     nand->buf_count         = 0;
>> +     nand->oob_size          = 0;
>> +     nand->use_ecc           = 0;
>> +     nand->is_ready          = 0;
>> +     nand->retcode           = ERR_NONE;
>> +     nand->data_size         = 0;
>> +     nand->use_dma           = 0;
>> +     nand->command           = command;
>>
>>       switch (command) {
>>       case NAND_CMD_READ0:
>>       case NAND_CMD_PAGEPROG:
>> -             info->use_ecc = 1;
>> +             nand->use_ecc = 1;
>>       case NAND_CMD_READOOB:
>>               pxa3xx_set_datasize(info);
>> +             nand->oob_buff = nand->data_buff + nand->data_size;
>> +             nand->use_dma = use_dma;
>>               break;
>>       case NAND_CMD_SEQIN:
>>               exec_cmd = 0;
>>               break;
>>       default:
>> -             info->ndcb1 = 0;
>> -             info->ndcb2 = 0;
>> +             nand->ndcb1 = 0;
>> +             nand->ndcb2 = 0;
>>               break;
>>       }
>>
>> -     info->ndcb0 = ndcb0;
>> +     nand->ndcb0 = ndcb0;
>>       addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
>>                                   + info->col_addr_cycles);
>>
>> @@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>       case NAND_CMD_READ0:
>>               cmd = info->cmdset->read1;
>>               if (command == NAND_CMD_READOOB)
>> -                     info->buf_start = mtd->writesize + column;
>> +                     nand->buf_start = mtd->writesize + column;
>>               else
>> -                     info->buf_start = column;
>> +                     nand->buf_start = column;
>>
>>               if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
>> -                     info->ndcb0 |= NDCB0_CMD_TYPE(0)
>> +                     nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>>                                       | addr_cycle
>>                                       | (cmd & NDCB0_CMD1_MASK);
>>               else
>> -                     info->ndcb0 |= NDCB0_CMD_TYPE(0)
>> +                     nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>>                                       | NDCB0_DBC
>>                                       | addr_cycle
>>                                       | cmd;
>> @@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>       case NAND_CMD_SEQIN:
>>               /* small page addr setting */
>>               if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
>> -                     info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>> +                     nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>>                                       | (column & 0xFF);
>>
>> -                     info->ndcb2 = 0;
>> +                     nand->ndcb2 = 0;
>>               } else {
>> -                     info->ndcb1 = ((page_addr & 0xFFFF) << 16)
>> +                     nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
>>                                       | (column & 0xFFFF);
>>
>>                       if (page_addr & 0xFF0000)
>> -                             info->ndcb2 = (page_addr & 0xFF0000) >> 16;
>> +                             nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
>>                       else
>> -                             info->ndcb2 = 0;
>> +                             nand->ndcb2 = 0;
>>               }
>>
>> -             info->buf_count = mtd->writesize + mtd->oobsize;
>> -             memset(info->data_buff, 0xFF, info->buf_count);
>> +             nand->buf_count = mtd->writesize + mtd->oobsize;
>> +             memset(nand->data_buff, 0xFF, nand->buf_count);
>>
>>               break;
>>
>>       case NAND_CMD_PAGEPROG:
>> -             if (is_buf_blank(info->data_buff,
>> +             if (is_buf_blank(nand->data_buff,
>>                                       (mtd->writesize + mtd->oobsize))) {
>>                       exec_cmd = 0;
>>                       break;
>>               }
>>
>>               cmd = info->cmdset->program;
>> -             info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>> +             nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>>                               | NDCB0_AUTO_RS
>>                               | NDCB0_ST_ROW_EN
>>                               | NDCB0_DBC
>> @@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>
>>       case NAND_CMD_READID:
>>               cmd = info->cmdset->read_id;
>> -             info->buf_count = info->read_id_bytes;
>> -             info->ndcb0 |= NDCB0_CMD_TYPE(3)
>> +             nand->buf_count = info->read_id_bytes;
>> +             nand->ndcb0 |= NDCB0_CMD_TYPE(3)
>>                               | NDCB0_ADDR_CYC(1)
>>                               | cmd;
>>
>> -             info->data_size = 8;
>> +             nand->data_size = 8;
>>               break;
>>       case NAND_CMD_STATUS:
>>               cmd = info->cmdset->read_status;
>> -             info->buf_count = 1;
>> -             info->ndcb0 |= NDCB0_CMD_TYPE(4)
>> +             nand->buf_count = 1;
>> +             nand->ndcb0 |= NDCB0_CMD_TYPE(4)
>>                               | NDCB0_ADDR_CYC(1)
>>                               | cmd;
>>
>> -             info->data_size = 8;
>> +             nand->data_size = 8;
>>               break;
>>
>>       case NAND_CMD_ERASE1:
>>               cmd = info->cmdset->erase;
>> -             info->ndcb0 |= NDCB0_CMD_TYPE(2)
>> +             nand->ndcb0 |= NDCB0_CMD_TYPE(2)
>>                               | NDCB0_AUTO_RS
>>                               | NDCB0_ADDR_CYC(3)
>>                               | NDCB0_DBC
>>                               | cmd;
>> -             info->ndcb1 = page_addr;
>> -             info->ndcb2 = 0;
>> +             nand->ndcb1 = page_addr;
>> +             nand->ndcb2 = 0;
>>
>>               break;
>>       case NAND_CMD_RESET:
>>               cmd = info->cmdset->reset;
>> -             info->ndcb0 |= NDCB0_CMD_TYPE(5)
>> +             nand->ndcb0 |= NDCB0_CMD_TYPE(5)
>>                               | cmd;
>>
>>               break;
>> @@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>>                               int column, int page_addr)
>>  {
>>       struct pxa3xx_nand_info *info = mtd->priv;
>> +     struct pxa3xx_nand *nand = info->nand_data;
>>       int ret, exec_cmd;
>>
>>       /*
>> @@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>>       if (info->reg_ndcr & NDCR_DWIDTH_M)
>>               column /= 2;
>>
>> -     exec_cmd = prepare_command_pool(info, command, column, page_addr);
>> +     /*
>> +      * There may be different NAND chip hooked to
>> +      * different chip select, so check whether
>> +      * chip select has been changed, if yes, reset the timing
>> +      */
>> +     if (nand->chip_select != info->chip_select) {
>> +             nand->chip_select = info->chip_select;
>> +             nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>> +             nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>> +     }
>> +
>> +     nand->state = STATE_PREPARED;
>> +     exec_cmd = prepare_command_pool(nand, command, column, page_addr);
>>       if (exec_cmd) {
>> -             init_completion(&info->cmd_complete);
>> -             pxa3xx_nand_start(info);
>> +             init_completion(&nand->cmd_complete);
>> +             pxa3xx_nand_start(nand);
>>
>> -             ret = wait_for_completion_timeout(&info->cmd_complete,
>> +             ret = wait_for_completion_timeout(&nand->cmd_complete,
>>                               CHIP_DELAY_TIMEOUT);
>>               if (!ret) {
>>                       printk(KERN_ERR "Wait time out!!!\n");
>>                       /* Stop State Machine for next command cycle */
>> -                     pxa3xx_nand_stop(info);
>> +                     pxa3xx_nand_stop(nand);
>>               }
>> -             info->state = STATE_IDLE;
>>       }
>> +     nand->state = STATE_IDLE;
>>  }
>>
>>  static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
>> @@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>>               struct nand_chip *chip, uint8_t *buf, int page)
>>  {
>>       struct pxa3xx_nand_info *info = mtd->priv;
>> +     struct pxa3xx_nand *nand = info->nand_data;
>>
>>       chip->read_buf(mtd, buf, mtd->writesize);
>>       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>>
>> -     if (info->retcode == ERR_SBERR) {
>> +     if (nand->retcode == ERR_SBERR) {
>>               switch (info->use_ecc) {
>>               case 1:
>>                       mtd->ecc_stats.corrected++;
>> @@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>>               default:
>>                       break;
>>               }
>> -     } else if (info->retcode == ERR_DBERR) {
>> +     } else if (nand->retcode == ERR_DBERR) {
>>               /*
>>                * for blank page (all 0xff), HW will calculate its ECC as
>>                * 0, which is different from the ECC information within
>>                * OOB, ignore such double bit errors
>>                */
>>               if (is_buf_blank(buf, mtd->writesize))
>> -                     info->retcode = ERR_NONE;
>> +                     nand->retcode = ERR_NONE;
>>               else
>>                       mtd->ecc_stats.failed++;
>>       }
>> @@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>>  static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>>  {
>>       struct pxa3xx_nand_info *info = mtd->priv;
>> +     struct pxa3xx_nand *nand = info->nand_data;
>>       char retval = 0xFF;
>>
>> -     if (info->buf_start < info->buf_count)
>> +     if (nand->buf_start < nand->buf_count)
>>               /* Has just send a new command? */
>> -             retval = info->data_buff[info->buf_start++];
>> +             retval = nand->data_buff[nand->buf_start++];
>>
>>       return retval;
>>  }
>> @@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>>  static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>>  {
>>       struct pxa3xx_nand_info *info = mtd->priv;
>> +     struct pxa3xx_nand *nand = info->nand_data;
>>       u16 retval = 0xFFFF;
>>
>> -     if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
>> -             retval = *((u16 *)(info->data_buff+info->buf_start));
>> -             info->buf_start += 2;
>> +     if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
>> +             retval = *((u16 *)(nand->data_buff+nand->buf_start));
>> +             nand->buf_start += 2;
>>       }
>>       return retval;
>>  }
>> @@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>>  static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>>  {
>>       struct pxa3xx_nand_info *info = mtd->priv;
>> -     int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>> +     struct pxa3xx_nand *nand = info->nand_data;
>> +     int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>>
>> -     memcpy(buf, info->data_buff + info->buf_start, real_len);
>> -     info->buf_start += real_len;
>> +     memcpy(buf, nand->data_buff + nand->buf_start, real_len);
>> +     nand->buf_start += real_len;
>>  }
>>
>>  static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
>>               const uint8_t *buf, int len)
>>  {
>>       struct pxa3xx_nand_info *info = mtd->priv;
>> -     int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>> +     struct pxa3xx_nand *nand = info->nand_data;
>> +     int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>>
>> -     memcpy(info->data_buff + info->buf_start, buf, real_len);
>> -     info->buf_start += real_len;
>> +     memcpy(nand->data_buff + nand->buf_start, buf, real_len);
>> +     nand->buf_start += real_len;
>>  }
>>
>>  static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
>> @@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>>  static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>>  {
>>       struct pxa3xx_nand_info *info = mtd->priv;
>> +     struct pxa3xx_nand *nand = info->nand_data;
>>
>>       /* pxa3xx_nand_send_command has waited for command complete */
>>       if (this->state == FL_WRITING || this->state == FL_ERASING) {
>> -             if (info->retcode == ERR_NONE)
>> +             if (nand->retcode == ERR_NONE)
>>                       return 0;
>>               else {
>>                       /*
>> @@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>>  static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>>                                   const struct pxa3xx_nand_flash *f)
>>  {
>> -     struct platform_device *pdev = info->pdev;
>> +     struct pxa3xx_nand *nand = info->nand_data;
>> +     struct platform_device *pdev = nand->pdev;
>>       struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>       uint32_t ndcr = 0x0; /* enable all interrupts */
>>
>> @@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>>       ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>>
>>       info->reg_ndcr = ndcr;
>> +     info->use_ecc = 1;
>>
>>       pxa3xx_nand_set_timing(info, f->timing);
>>       return 0;
>> @@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>>
>>  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>>  {
>> -     uint32_t ndcr = nand_readl(info, NDCR);
>> +     struct pxa3xx_nand *nand = info->nand_data;
>> +     uint32_t ndcr = nand_readl(nand, NDCR);
>> +
>> +     if (info->chip_select > 0) {
>> +             printk(KERN_ERR "We could not detect configure"
>> +                             " if more than one cs is supported!!\n");
>> +             BUG();
>
> like Daniel already noticed, may be dev_err() is enough?
Would change to dev_err();

>
>> +     }
>>       info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
>>       /* set info fields needed to read id */
>>       info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
>>       info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
>>       info->cmdset = &default_cmdset;
>>
>> -     info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
>> -     info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
>> +     info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
>> +     info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
>>
>>       return 0;
>>  }
>> @@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>>   */
>>  #define MAX_BUFF_SIZE        PAGE_SIZE
>>
>> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
>> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
>>  {
>> -     struct platform_device *pdev = info->pdev;
>> -     int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>> -
>> -     if (use_dma == 0) {
>> -             info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
>> -             if (info->data_buff == NULL)
>> -                     return -ENOMEM;
>> -             return 0;
>> -     }
>> -
>> -     info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
>> -                             &info->data_buff_phys, GFP_KERNEL);
>> -     if (info->data_buff == NULL) {
>> -             dev_err(&pdev->dev, "failed to allocate dma buffer\n");
>> -             return -ENOMEM;
>> -     }
>> -
>> -     info->data_buff_size = MAX_BUFF_SIZE;
>> -     info->data_desc = (void *)info->data_buff + data_desc_offset;
>> -     info->data_desc_addr = info->data_buff_phys + data_desc_offset;
>> +     struct pxa3xx_nand *nand;
>> +     struct mtd_info *mtd;
>>
>> -     info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
>> -                             pxa3xx_nand_data_dma_irq, info);
>> -     if (info->data_dma_ch < 0) {
>> -             dev_err(&pdev->dev, "failed to request data dma\n");
>> -             dma_free_coherent(&pdev->dev, info->data_buff_size,
>> -                             info->data_buff, info->data_buff_phys);
>> -             return info->data_dma_ch;
>> -     }
>> +     if (!info)
>> +             return;
>>
>> -     return 0;
>> +     nand = info->nand_data;
>> +     mtd = get_mtd_by_info(info);
>> +     kfree(mtd);
>> +     nand->info[cs] = NULL;
>>  }
>>
>>  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>>  {
>> -     struct mtd_info *mtd = info->mtd;
>> +     struct pxa3xx_nand *nand = info->nand_data;
>> +     struct mtd_info *mtd = get_mtd_by_info(info);
>>       struct nand_chip *chip = mtd->priv;
>>
>>       /* use the common timing to make a try */
>> -     pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
>> +     if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
>> +             return 0;
>>       chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
>> -     if (info->is_ready)
>> +     if (nand->is_ready)
>>               return 1;
>>       else
>>               return 0;
>
> I think it is time to change this function return convention to propagate
> errors and not just 0 or 1, (may be in separate patch) what do you think?

How about return 0 when sensing the READY signal, or return -ENODEV?

>
>> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>>  static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>  {
>>       struct pxa3xx_nand_info *info = mtd->priv;
>> -     struct platform_device *pdev = info->pdev;
>> +     struct pxa3xx_nand *nand = info->nand_data;
>> +     struct platform_device *pdev = nand->pdev;
>>       struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>       struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>>       const struct pxa3xx_nand_flash *f = NULL;
>> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>       uint64_t chipsize;
>>       int i, ret, num;
>>
>> +     nand->chip_select = info->chip_select;
>>       if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>>               goto KEEP_CONFIG;
>>
>>       ret = pxa3xx_nand_sensing(info);
>>       if (!ret) {
>> -             kfree(mtd);
>> -             info->mtd = NULL;
>> -             printk(KERN_INFO "There is no nand chip on cs 0!\n");
>> +             free_cs_resource(info, nand->chip_select);
>> +             printk(KERN_INFO "There is no nand chip on cs %d!\n",
>> +                             nand->chip_select);
>>
>>               return -EINVAL;
>>       }
>>
>>       chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>> -     id = *((uint16_t *)(info->data_buff));
>> +     id = *((uint16_t *)(nand->data_buff));
>>       if (id != 0)
>>               printk(KERN_INFO "Detect a flash id %x\n", id);
>>       else {
>> -             kfree(mtd);
>> -             info->mtd = NULL;
>> -             printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
>
> Is this warning no longer needed?
Would readd it in the next post.

>
>> -
>> +             free_cs_resource(info, nand->chip_select);
>>               return -EINVAL;
>>       }
>>
>> @@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>       }
>>
>>       if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
>> -             kfree(mtd);
>> -             info->mtd = NULL;
>> +             free_cs_resource(info, nand->chip_select);
>>               printk(KERN_ERR "ERROR!! flash not defined!!!\n");
>>
>>               return -EINVAL;
>>       }
>>
>> -     pxa3xx_nand_config_flash(info, f);
>> +     if (pxa3xx_nand_config_flash(info, f)) {
>> +             printk(KERN_ERR "ERROR! Configure failed\n");
>> +             return -EINVAL;
>> +     }
>
> Although, the pxa3xx_nand_config_flash() returns only 0 or -EINVAL,
> it is better to propagate its return value.

Ok, would apply this.

>
>>       pxa3xx_flash_ids[0].name = f->name;
>>       pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
>>       pxa3xx_flash_ids[0].pagesize = f->page_size;
>> @@ -950,13 +977,13 @@ KEEP_CONFIG:
>>       if (nand_scan_ident(mtd, 1, def))
>>               return -ENODEV;
>>       /* calculate addressing information */
>> +     nand->oob_buff = nand->data_buff + mtd->writesize;
>>       info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
>> -     info->oob_buff = info->data_buff + mtd->writesize;
>>       if ((mtd->size >> chip->page_shift) > 65536)
>>               info->row_addr_cycles = 3;
>>       else
>>               info->row_addr_cycles = 2;
>> -     mtd->name = mtd_names[0];
>> +     mtd->name = mtd_names[nand->chip_select];
>>       chip->ecc.mode = NAND_ECC_HW;
>>       chip->ecc.size = info->page_size;
>>
>> @@ -967,51 +994,33 @@ KEEP_CONFIG:
>>       return nand_scan_tail(mtd);
>>  }
>>
>> -static
>> -struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>> +static int alloc_nand_resource(struct platform_device *pdev)
>>  {
>> +     struct pxa3xx_nand_platform_data *pdata;
>>       struct pxa3xx_nand_info *info;
>>       struct nand_chip *chip;
>>       struct mtd_info *mtd;
>> +     struct pxa3xx_nand *nand;
>>       struct resource *r;
>> -     int ret, irq;
>> +     int ret, irq, cs;
>> +     int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>>
>> -     mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
>> -                     GFP_KERNEL);
>> -     if (!mtd) {
>> +     pdata = pdev->dev.platform_data;
>> +     nand = kzalloc(sizeof(struct mtd_info)
>> +                     + sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
>> +     if (!nand) {
>>               dev_err(&pdev->dev, "failed to allocate memory\n");
>> -             return NULL;
>> +             return -ENOMEM;
>>       }
>>
>> -     info = (struct pxa3xx_nand_info *)(&mtd[1]);
>> -     chip = (struct nand_chip *)(&mtd[1]);
>> -     info->pdev = pdev;
>> -     info->mtd = mtd;
>> -     mtd->priv = info;
>> -     mtd->owner = THIS_MODULE;
>> -
>> -     chip->ecc.read_page     = pxa3xx_nand_read_page_hwecc;
>> -     chip->ecc.write_page    = pxa3xx_nand_write_page_hwecc;
>> -     chip->controller        = &info->controller;
>> -     chip->waitfunc          = pxa3xx_nand_waitfunc;
>> -     chip->select_chip       = pxa3xx_nand_select_chip;
>> -     chip->dev_ready         = pxa3xx_nand_dev_ready;
>> -     chip->cmdfunc           = pxa3xx_nand_cmdfunc;
>> -     chip->read_word         = pxa3xx_nand_read_word;
>> -     chip->read_byte         = pxa3xx_nand_read_byte;
>> -     chip->read_buf          = pxa3xx_nand_read_buf;
>> -     chip->write_buf         = pxa3xx_nand_write_buf;
>> -     chip->verify_buf        = pxa3xx_nand_verify_buf;
>> -
>> -     spin_lock_init(&chip->controller->lock);
>> -     init_waitqueue_head(&chip->controller->wq);
>> -     info->clk = clk_get(&pdev->dev, NULL);
>> -     if (IS_ERR(info->clk)) {
>> +     nand->pdev = pdev;
>> +     nand->clk = clk_get(&pdev->dev, NULL);
>> +     if (IS_ERR(nand->clk)) {
>>               dev_err(&pdev->dev, "failed to get nand clock\n");
>> -             ret = PTR_ERR(info->clk);
>> -             goto fail_free_mtd;
>> +             ret = PTR_ERR(nand->clk);
>> +             goto fail_alloc;
>>       }
>> -     clk_enable(info->clk);
>> +     clk_enable(nand->clk);
>>
>>       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>>       if (r == NULL) {
>> @@ -1019,7 +1028,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>>               ret = -ENXIO;
>>               goto fail_put_clk;
>>       }
>> -     info->drcmr_dat = r->start;
>> +     nand->drcmr_dat = r->start;
>>
>>       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
>>       if (r == NULL) {
>> @@ -1027,7 +1036,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>>               ret = -ENXIO;
>>               goto fail_put_clk;
>>       }
>> -     info->drcmr_cmd = r->start;
>> +     nand->drcmr_cmd = r->start;
>>
>>       irq = platform_get_irq(pdev, 0);
>>       if (irq < 0) {
>> @@ -1050,81 +1059,147 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>>               goto fail_put_clk;
>>       }
>>
>> -     info->mmio_base = ioremap(r->start, resource_size(r));
>> -     if (info->mmio_base == NULL) {
>> +     nand->mmio_base = ioremap(r->start, resource_size(r));
>> +     if (nand->mmio_base == NULL) {
>>               dev_err(&pdev->dev, "ioremap() failed\n");
>>               ret = -ENODEV;
>>               goto fail_free_res;
>>       }
>> -     info->mmio_phys = r->start;
>> -
>> -     ret = pxa3xx_nand_init_buff(info);
>> -     if (ret)
>> -             goto fail_free_io;
>> +     nand->mmio_phys = r->start;
>>
>>       /* initialize all interrupts to be disabled */
>> -     disable_int(info, NDSR_MASK);
>> +     disable_int(nand, NDSR_MASK);
>>
>>       ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED,
>> -                       pdev->name, info);
>> +                       pdev->name, nand);
>>       if (ret < 0) {
>>               dev_err(&pdev->dev, "failed to request IRQ\n");
>> -             goto fail_free_buf;
>> +             ret = -ENXIO;
>> +             goto fail_free_io;
>> +     }
>> +
>> +     platform_set_drvdata(pdev, nand);
>> +
>> +     spin_lock_init(&nand->controller.lock);
>> +     init_waitqueue_head(&nand->controller.wq);
>> +     for (cs = 0; cs < pdata->cs_num; cs++) {
>> +             mtd = kzalloc(sizeof(struct mtd_info)
>> +                             + sizeof(struct pxa3xx_nand_info),
>> +                             GFP_KERNEL);
>> +             if (!mtd) {
>> +                     dev_err(&pdev->dev, "failed to allocate memory\n");
>> +                     ret = -ENOMEM;
>> +                     goto fail_free_irq;
>> +             }
>> +
>> +             info = (struct pxa3xx_nand_info *)(&mtd[1]);
>> +             info->nand_data = nand;
>> +             info->chip_select = cs;
>> +             mtd->priv = info;
>> +             mtd->owner = THIS_MODULE;
>> +             nand->info[cs] = info;
>> +
>> +             chip = (struct nand_chip *)(&mtd[1]);
>> +             chip->controller        = &nand->controller;
>> +             chip->ecc.read_page     = pxa3xx_nand_read_page_hwecc;
>> +             chip->ecc.write_page    = pxa3xx_nand_write_page_hwecc;
>> +             chip->waitfunc          = pxa3xx_nand_waitfunc;
>> +             chip->select_chip       = pxa3xx_nand_select_chip;
>> +             chip->cmdfunc           = pxa3xx_nand_cmdfunc;
>> +             chip->read_word         = pxa3xx_nand_read_word;
>> +             chip->read_byte         = pxa3xx_nand_read_byte;
>> +             chip->read_buf          = pxa3xx_nand_read_buf;
>> +             chip->write_buf         = pxa3xx_nand_write_buf;
>> +             chip->verify_buf        = pxa3xx_nand_verify_buf;
>>       }
>>
>> -     platform_set_drvdata(pdev, info);
>> +     if (use_dma == 0) {
>> +             nand->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
>> +             if (nand->data_buff == NULL) {
>> +                     ret = -ENOMEM;
>> +                     goto fail_free_buf;
>> +             }
>> +             goto success_exit;
>> +     }
>>
>> -     return info;
>> +     nand->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
>> +                     &nand->data_buff_phys, GFP_KERNEL);
>> +     if (nand->data_buff == NULL) {
>> +             dev_err(&pdev->dev, "failed to allocate dma buffer\n");
>> +             ret = -ENOMEM;
>> +             goto fail_free_buf;
>> +     }
>>
>> +     nand->data_desc = (void *)nand->data_buff + data_desc_offset;
>> +     nand->data_desc_addr = nand->data_buff_phys + data_desc_offset;
>> +     nand->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
>> +                     pxa3xx_nand_data_dma_irq, nand);
>> +     if (nand->data_dma_ch < 0) {
>> +             dev_err(&pdev->dev, "failed to request data dma\n");
>> +             ret = -ENXIO;
>> +             goto fail_free_dma_buf;
>> +     }
>> +success_exit:
>> +     return 0;
>> +
>> +fail_free_dma_buf:
>> +     dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
>> +                     nand->data_buff, nand->data_buff_phys);
>>  fail_free_buf:
>> -     free_irq(irq, info);
>> -     if (use_dma) {
>> -             pxa_free_dma(info->data_dma_ch);
>> -             dma_free_coherent(&pdev->dev, info->data_buff_size,
>> -                     info->data_buff, info->data_buff_phys);
>> -     } else
>> -             kfree(info->data_buff);
>> +     for (cs = 0; cs < pdata->cs_num; cs++) {
>> +             info = nand->info[cs];
>> +             free_cs_resource(info, cs);
>> +     }
>> +fail_free_irq:
>> +     free_irq(irq, nand);
>>  fail_free_io:
>> -     iounmap(info->mmio_base);
>> +     iounmap(nand->mmio_base);
>>  fail_free_res:
>>       release_mem_region(r->start, resource_size(r));
>>  fail_put_clk:
>> -     clk_disable(info->clk);
>> -     clk_put(info->clk);
>> -fail_free_mtd:
>> -     kfree(mtd);
>> -     return NULL;
>> +     clk_disable(nand->clk);
>> +     clk_put(nand->clk);
>> +fail_alloc:
>> +     kfree(nand);
>> +     return ret;
>>  }
>>
>>  static int pxa3xx_nand_remove(struct platform_device *pdev)
>>  {
>> -     struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>> -     struct mtd_info *mtd = info->mtd;
>> +     struct pxa3xx_nand *nand = platform_get_drvdata(pdev);
>> +     struct pxa3xx_nand_platform_data *pdata;
>> +     struct pxa3xx_nand_info *info;
>> +     struct mtd_info *mtd;
>>       struct resource *r;
>> -     int irq;
>> +     int irq, cs;
>>
>>       platform_set_drvdata(pdev, NULL);
>> +     pdata = pdev->dev.platform_data;
>>
>>       irq = platform_get_irq(pdev, 0);
>>       if (irq >= 0)
>> -             free_irq(irq, info);
>> +             free_irq(irq, nand);
>>       if (use_dma) {
>> -             pxa_free_dma(info->data_dma_ch);
>> -             dma_free_writecombine(&pdev->dev, info->data_buff_size,
>> -                             info->data_buff, info->data_buff_phys);
>> +             pxa_free_dma(nand->data_dma_ch);
>> +             dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
>> +                             nand->data_buff, nand->data_buff_phys);
>>       } else
>> -             kfree(info->data_buff);
>> +             kfree(nand->data_buff);
>>
>> -     iounmap(info->mmio_base);
>> +     iounmap(nand->mmio_base);
>>       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>       release_mem_region(r->start, resource_size(r));
>>
>> -     clk_disable(info->clk);
>> -     clk_put(info->clk);
>> +     clk_disable(nand->clk);
>> +     clk_put(nand->clk);
>>
>> -     if (mtd) {
>> +     for (cs = 0; cs < pdata->cs_num; cs++) {
>> +             info = nand->info[cs];
>> +             if (!info)
>> +                     continue;
>> +             mtd = get_mtd_by_info(info);
>>               mtd_device_unregister(mtd);
>> -             kfree(mtd);
>> +             free_cs_resource(info, cs);
>>       }
>>       return 0;
>>  }
>> @@ -1134,34 +1209,54 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
>>       struct pxa3xx_nand_platform_data *pdata;
>>       struct pxa3xx_nand_info *info;
>>
>> +     struct pxa3xx_nand *nand;
>> +     struct mtd_info *mtd;
>> +     int cs, ret, nr_parts, probe_success;
>> +
>> +     probe_success = 0;
>
> Can this be done along with declaration?
Ok, would apply to the next post.

>
>>       pdata = pdev->dev.platform_data;
>>       if (!pdata) {
>>               dev_err(&pdev->dev, "no platform data defined\n");
>>               return -ENODEV;
>>       }
>>
>> -     info = alloc_nand_resource(pdev);
>> -     if (info == NULL)
>> +     ret = alloc_nand_resource(pdev);
>> +     if (ret)
>>               return -ENOMEM;
>
> Why not propagate the return value of alloc_nand_resource()?

Would apply propagate error to the next post.

>
>>
>> -     if (pxa3xx_nand_scan(info->mtd)) {
>> -             dev_err(&pdev->dev, "failed to scan nand\n");
>> -             pxa3xx_nand_remove(pdev);
>> -             return -ENODEV;
>> -     }
>> +     nand = platform_get_drvdata(pdev);
>> +     for (cs = 0; cs < pdata->cs_num; cs++) {
>> +             info = nand->info[cs];
>> +             mtd = get_mtd_by_info(info);
>> +             if (pxa3xx_nand_scan(mtd)) {
>> +                     dev_err(&pdev->dev, "failed to scan nand\n");
>
> I think, it would be useful also to print here the return value of pxa3xx_nand_scan().
Ok, would apply to the next post

>
>> +                     continue;
>> +             }
>> +
>> +             ret = 0;
>> +             nr_parts = 0;
>> +             if (mtd_has_cmdlinepart()) {
>> +                     const char *probes[] = { "cmdlinepart", NULL };
>> +                     struct mtd_partition *parts;
>>
>> -     if (mtd_has_cmdlinepart()) {
>> -             const char *probes[] = { "cmdlinepart", NULL };
>> -             struct mtd_partition *parts;
>> -             int nr_parts;
>> +                     nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0);
>>
>> -             nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0);
>> +                     if (nr_parts)
>> +                             ret = mtd_device_register(mtd, parts, nr_parts);
>> +             }
>>
>> -             if (nr_parts)
>> -                     return mtd_device_register(info->mtd, parts, nr_parts);
>> +             if (!nr_parts)
>> +                     ret = mtd_device_register(mtd, pdata->parts[cs],
>> +                                     pdata->nr_parts[cs]);
>> +             if (!ret)
>> +                     probe_success = 1;
>>       }
>>
>> -     return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
>> +     if (!probe_success) {
>> +             pxa3xx_nand_remove(pdev);
>> +             return -ENODEV;
>> +     } else
>
> You don't need this else statement

Would apply to the next post.

>
>> +             return 0;
>>  }
>>
>>  #ifdef CONFIG_PM
>> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>       struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>       struct mtd_info *mtd = info->mtd;
>>
>> -     nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>> -     nand_writel(info, NDTR1CS0, info->ndtr1cs0);
>> +     nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>> +     nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>>       clk_enable(info->clk);
>>
>>       return 0;
>
> I won't be able to test the patch in the near future, sorry...
>

Thanks for comments.

Best regards,
Lei

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-08 15:26     ` Lei Wen
@ 2011-06-23 10:44       ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-06-23 10:44 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

Hi Lei,

On 06/23/11 09:35, Lei Wen wrote:
> Hi Igor,
>
> On Wed, Jun 22, 2011 at 9:45 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 06/22/11 06:17, Lei Wen wrote:
>>> Current pxa3xx_nand controller has two chip select which
>>> both be workable. This patch enable this feature.
>>>
>>> Update platform driver to support this feature.
>>>
>>> Another 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.
>> You should _also_ put this comment inside the pxa3xx_nand.h
>> may be even inside the pxa3xx_nand_platform_data structure,
>> so people would not have to search the git log to find this problem.
> How about just throw out a BUG() to force people cannot set keep_config
> when supported cs num more than 1? Also appended with the comments above?

I don't think BUG() is a good idea, instead what you can do
is force the user of the _new_ feature to _not_ use the keep_config
by overriding the keep_config to 0 and printing a warning
that keep_config is prohibited with multiple chip selects feature and
that it will be disabled.
Of course you will still need to put a comment inside the pxa3xx_nand.h.

>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>> ---
>>>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>>>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>>>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>>>  arch/arm/mach-pxa/littleton.c                |    5 +-
>>>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>>>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>>>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>>>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>>>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>>>  9 files changed, 444 insertions(+), 338 deletions(-)

[...]

>>> diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>>> index 442301f..34a3f52 100644
>>> --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>>> +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>>> @@ -41,6 +41,8 @@ struct pxa3xx_nand_flash {
>>>       struct pxa3xx_nand_timing *timing;      /* NAND Flash timing */
>>>  };
>>>
>>> +/* The max num of chip select current support */
>> /* The maximum number of chip selects currently supported */
>>
>>> +#define NUM_CHIP_SELECT              (2)
>>>  struct pxa3xx_nand_platform_data {
>>>
>>>       /* the data flash bus is shared between the Static Memory
>>> @@ -52,8 +54,10 @@ struct pxa3xx_nand_platform_data {
>>>       /* allow platform code to keep OBM/bootloader defined NFC config */
>>>       int     keep_config;
>>>
>>> -     const struct mtd_partition              *parts;
>>> -     unsigned int                            nr_parts;
>>> +     /* indicate how many chip select would be used for this platform */
>> /* indicate how many chip selects will be used */
>>
>>> +     int     cs_num;
>> This name is too confusing, I think even num_cs is better or cs_count?
>> Also, may be align it with the others?
>  I prefer the num_cs.

Good.

>>> +     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;
>>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
>>> index 30689cc..259b8d5 100644
>>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>>> +++ b/drivers/mtd/nand/pxa3xx_nand.c

[...]

>>> @@ -123,63 +126,63 @@ enum {
>>>  struct pxa3xx_nand_info {
>>>       struct nand_chip        nand_chip;
>>>
>>> -     struct nand_hw_control  controller;
>>> -     struct platform_device   *pdev;
>>>       struct pxa3xx_nand_cmdset *cmdset;
>>> +     /* page size of attached chip */
>>> +     uint16_t                page_size;
>>> +     uint8_t                 chip_select;
>>> +     uint8_t                 use_ecc;
>>> +
>>> +     /* calculated from pxa3xx_nand_flash data */
>>> +     uint8_t                 col_addr_cycles;
>>> +     uint8_t                 row_addr_cycles;
>>> +     uint8_t                 read_id_bytes;
>>> +
>>> +     /* cached register value */
>>> +     uint32_t                reg_ndcr;
>>> +     uint32_t                ndtr0cs0;
>>> +     uint32_t                ndtr1cs0;
>>>
>>> +     void                    *nand_data;
>>> +};
>>> +
>>> +struct pxa3xx_nand {
>>>       struct clk              *clk;
>>>       void __iomem            *mmio_base;
>>>       unsigned long           mmio_phys;
>>> +     struct nand_hw_control  controller;
>>> +     struct completion       cmd_complete;
>>> +     struct platform_device   *pdev;
>> please, align
> Do you still mean cs_num? What this comment refer to?

No, I mean the white space before *pdev - it is not aligned with all others

>>> -     unsigned int            buf_start;
>>> -     unsigned int            buf_count;
>>> -
>>> -     struct mtd_info         *mtd;
>>>       /* DMA information */
>>>       int                     drcmr_dat;
>>>       int                     drcmr_cmd;
>>> -
>>> -     unsigned char           *data_buff;
>>> -     unsigned char           *oob_buff;
>>> -     dma_addr_t              data_buff_phys;
>>> -     size_t                  data_buff_size;
>>>       int                     data_dma_ch;
>>> -     struct pxa_dma_desc     *data_desc;
>>> +     dma_addr_t              data_buff_phys;
>>>       dma_addr_t              data_desc_addr;
>>> +     struct pxa_dma_desc     *data_desc;
>>>
>>> -     uint32_t                reg_ndcr;
>>> -
>>> -     /* saved column/page_addr during CMD_SEQIN */
>>> -     int                     seqin_column;
>>> -     int                     seqin_page_addr;
>>> +     struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
>>> +     uint32_t                command;
>>> +     uint16_t                data_size;      /* data size in FIFO */
>>> +     uint16_t                oob_size;
>>> +     unsigned char           *data_buff;
>>> +     unsigned char           *oob_buff;
>>> +     uint32_t                buf_start;
>>> +     uint32_t                buf_count;
>>>
>>>       /* relate to the command */
>>>       unsigned int            state;
>>> -
>>> +     uint8_t                 chip_select;
>>>       int                     use_ecc;        /* use HW ECC ? */
>>>       int                     use_dma;        /* use DMA ? */
>>>       int                     is_ready;
>>> -
>>> -     unsigned int            page_size;      /* page size of attached chip */
>>> -     unsigned int            data_size;      /* data size in FIFO */
>>>       int                     retcode;
>>> -     struct completion       cmd_complete;
>>>
>>>       /* generated NDCBx register values */
>>> +     uint8_t                 total_cmds;
>>>       uint32_t                ndcb0;
>>>       uint32_t                ndcb1;
>>>       uint32_t                ndcb2;
>>> -
>>> -     /* timing calcuted from setting */
>>> -     uint32_t                ndtr0cs0;
>>> -     uint32_t                ndtr1cs0;
>>> -
>>> -     /* calculated from pxa3xx_nand_flash data */
>>> -     size_t          oob_size;
>>> -     size_t          read_id_bytes;
>>> -
>>> -     unsigned int    col_addr_cycles;
>>> -     unsigned int    row_addr_cycles;
>>>  };
>> It looks like if you switch the names of the structures above,
>> then the patch will be much shorter and cleaner,
>> but will it make structures meaning confusion?
> Could you elaborate more about this? Do you means no need to create a seperated
> structure?

No, if you have 2 chips, then certainly you'd better have a separate
structure for the chip description.

What I was wondering, is that there are many places in the patch where
you change the "info" to "nand", may be this can be avoided if you keep
the structure that describes the controller ("pxa3xx_nand" in the patch)
named "pxa3xx_nand_info" and the new structure that describes the chip
will be named something else like pxa3xx_nand_chip or anything like this.
This way, your patch will not have to change the "info" to "nand" in many
places (or maybe even all places) and will be much shorter and will include
only functional changes.



[...]

>>>  static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>>>  {
>>> -     struct pxa3xx_nand_info *info = devid;
>>> -     unsigned int status, is_completed = 0;
>>> +     struct pxa3xx_nand *nand = devid;
>>> +     struct pxa3xx_nand_info *info;
>>> +     unsigned int status, is_completed = 0, cs;
>>> +     unsigned int ready, cmd_done, page_done, badblock_detect;
>>>
>>> -     status = nand_readl(info, NDSR);
>>> +     cs              = nand->chip_select;
>>> +     ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
>>> +     cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
>>> +     page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
>>> +     badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
>> This is confusing... do you use to ?: operator for differentiating between
>> cs = 0 and cs = 1?
>> I think this is a bad idea.
>> Moreover, the use of ?: is discouraged among the kernel.
> I could use if/else to replace this statement.

or switch, what ever you like, but please, don't leave it like
if (cs) ..., because this is confusing it has a meaning of "cs is good or bad?",
while actually, you want to know if it is 0 or 1 (both values are good).


[...]

>>> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>>>               uint16_t column, int page_addr)
>>>  {
>>>       uint16_t cmd;
>>>       int addr_cycle, exec_cmd, ndcb0;
>>> -     struct mtd_info *mtd = info->mtd;
>>> +     struct mtd_info *mtd;
>>> +     struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>>>
>>> -     ndcb0 = 0;
>>> +     mtd = get_mtd_by_info(info);
>>> +     ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
>> This one is confusing too...
>> Besides, you don't need the parenthesis.
> Got that. I would fix it.

Good, my previous explanation also applies here.
Thanks


[...]

>>>  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>>>  {
>>> -     struct mtd_info *mtd = info->mtd;
>>> +     struct pxa3xx_nand *nand = info->nand_data;
>>> +     struct mtd_info *mtd = get_mtd_by_info(info);
>>>       struct nand_chip *chip = mtd->priv;
>>>
>>>       /* use the common timing to make a try */
>>> -     pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
>>> +     if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
>>> +             return 0;
>>>       chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
>>> -     if (info->is_ready)
>>> +     if (nand->is_ready)
>>>               return 1;
>>>       else
>>>               return 0;
>> I think it is time to change this function return convention to propagate
>> errors and not just 0 or 1, (may be in separate patch) what do you think?
> How about return 0 when sensing the READY signal, or return -ENODEV?

Yes, this should be fine, but also don't forget about
pxa3xx_nand_config_flash() function call - it can return 0 or -EINVAL
and it should also propagate instead of being squashed.

>>> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>>>  static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>>  {
>>>       struct pxa3xx_nand_info *info = mtd->priv;
>>> -     struct platform_device *pdev = info->pdev;
>>> +     struct pxa3xx_nand *nand = info->nand_data;
>>> +     struct platform_device *pdev = nand->pdev;
>>>       struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>>       struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>>>       const struct pxa3xx_nand_flash *f = NULL;
>>> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>>       uint64_t chipsize;
>>>       int i, ret, num;
>>>
>>> +     nand->chip_select = info->chip_select;
>>>       if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>>>               goto KEEP_CONFIG;
>>>
>>>       ret = pxa3xx_nand_sensing(info);
>>>       if (!ret) {
>>> -             kfree(mtd);
>>> -             info->mtd = NULL;
>>> -             printk(KERN_INFO "There is no nand chip on cs 0!\n");
>>> +             free_cs_resource(info, nand->chip_select);
>>> +             printk(KERN_INFO "There is no nand chip on cs %d!\n",
>>> +                             nand->chip_select);
>>>
>>>               return -EINVAL;

after the change of pxa3xx_nand_sensing() function,
you should change the above "return -EINVAL;" into "return ret;"



[...]


-- 
Regards,
Igor.

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-23 10:44       ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-06-23 10:44 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lei,

On 06/23/11 09:35, Lei Wen wrote:
> Hi Igor,
>
> On Wed, Jun 22, 2011 at 9:45 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 06/22/11 06:17, Lei Wen wrote:
>>> Current pxa3xx_nand controller has two chip select which
>>> both be workable. This patch enable this feature.
>>>
>>> Update platform driver to support this feature.
>>>
>>> Another 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.
>> You should _also_ put this comment inside the pxa3xx_nand.h
>> may be even inside the pxa3xx_nand_platform_data structure,
>> so people would not have to search the git log to find this problem.
> How about just throw out a BUG() to force people cannot set keep_config
> when supported cs num more than 1? Also appended with the comments above?

I don't think BUG() is a good idea, instead what you can do
is force the user of the _new_ feature to _not_ use the keep_config
by overriding the keep_config to 0 and printing a warning
that keep_config is prohibited with multiple chip selects feature and
that it will be disabled.
Of course you will still need to put a comment inside the pxa3xx_nand.h.

>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>> ---
>>>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>>>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>>>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>>>  arch/arm/mach-pxa/littleton.c                |    5 +-
>>>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>>>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>>>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>>>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>>>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>>>  9 files changed, 444 insertions(+), 338 deletions(-)

[...]

>>> diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>>> index 442301f..34a3f52 100644
>>> --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>>> +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>>> @@ -41,6 +41,8 @@ struct pxa3xx_nand_flash {
>>>       struct pxa3xx_nand_timing *timing;      /* NAND Flash timing */
>>>  };
>>>
>>> +/* The max num of chip select current support */
>> /* The maximum number of chip selects currently supported */
>>
>>> +#define NUM_CHIP_SELECT              (2)
>>>  struct pxa3xx_nand_platform_data {
>>>
>>>       /* the data flash bus is shared between the Static Memory
>>> @@ -52,8 +54,10 @@ struct pxa3xx_nand_platform_data {
>>>       /* allow platform code to keep OBM/bootloader defined NFC config */
>>>       int     keep_config;
>>>
>>> -     const struct mtd_partition              *parts;
>>> -     unsigned int                            nr_parts;
>>> +     /* indicate how many chip select would be used for this platform */
>> /* indicate how many chip selects will be used */
>>
>>> +     int     cs_num;
>> This name is too confusing, I think even num_cs is better or cs_count?
>> Also, may be align it with the others?
>  I prefer the num_cs.

Good.

>>> +     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;
>>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
>>> index 30689cc..259b8d5 100644
>>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>>> +++ b/drivers/mtd/nand/pxa3xx_nand.c

[...]

>>> @@ -123,63 +126,63 @@ enum {
>>>  struct pxa3xx_nand_info {
>>>       struct nand_chip        nand_chip;
>>>
>>> -     struct nand_hw_control  controller;
>>> -     struct platform_device   *pdev;
>>>       struct pxa3xx_nand_cmdset *cmdset;
>>> +     /* page size of attached chip */
>>> +     uint16_t                page_size;
>>> +     uint8_t                 chip_select;
>>> +     uint8_t                 use_ecc;
>>> +
>>> +     /* calculated from pxa3xx_nand_flash data */
>>> +     uint8_t                 col_addr_cycles;
>>> +     uint8_t                 row_addr_cycles;
>>> +     uint8_t                 read_id_bytes;
>>> +
>>> +     /* cached register value */
>>> +     uint32_t                reg_ndcr;
>>> +     uint32_t                ndtr0cs0;
>>> +     uint32_t                ndtr1cs0;
>>>
>>> +     void                    *nand_data;
>>> +};
>>> +
>>> +struct pxa3xx_nand {
>>>       struct clk              *clk;
>>>       void __iomem            *mmio_base;
>>>       unsigned long           mmio_phys;
>>> +     struct nand_hw_control  controller;
>>> +     struct completion       cmd_complete;
>>> +     struct platform_device   *pdev;
>> please, align
> Do you still mean cs_num? What this comment refer to?

No, I mean the white space before *pdev - it is not aligned with all others

>>> -     unsigned int            buf_start;
>>> -     unsigned int            buf_count;
>>> -
>>> -     struct mtd_info         *mtd;
>>>       /* DMA information */
>>>       int                     drcmr_dat;
>>>       int                     drcmr_cmd;
>>> -
>>> -     unsigned char           *data_buff;
>>> -     unsigned char           *oob_buff;
>>> -     dma_addr_t              data_buff_phys;
>>> -     size_t                  data_buff_size;
>>>       int                     data_dma_ch;
>>> -     struct pxa_dma_desc     *data_desc;
>>> +     dma_addr_t              data_buff_phys;
>>>       dma_addr_t              data_desc_addr;
>>> +     struct pxa_dma_desc     *data_desc;
>>>
>>> -     uint32_t                reg_ndcr;
>>> -
>>> -     /* saved column/page_addr during CMD_SEQIN */
>>> -     int                     seqin_column;
>>> -     int                     seqin_page_addr;
>>> +     struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
>>> +     uint32_t                command;
>>> +     uint16_t                data_size;      /* data size in FIFO */
>>> +     uint16_t                oob_size;
>>> +     unsigned char           *data_buff;
>>> +     unsigned char           *oob_buff;
>>> +     uint32_t                buf_start;
>>> +     uint32_t                buf_count;
>>>
>>>       /* relate to the command */
>>>       unsigned int            state;
>>> -
>>> +     uint8_t                 chip_select;
>>>       int                     use_ecc;        /* use HW ECC ? */
>>>       int                     use_dma;        /* use DMA ? */
>>>       int                     is_ready;
>>> -
>>> -     unsigned int            page_size;      /* page size of attached chip */
>>> -     unsigned int            data_size;      /* data size in FIFO */
>>>       int                     retcode;
>>> -     struct completion       cmd_complete;
>>>
>>>       /* generated NDCBx register values */
>>> +     uint8_t                 total_cmds;
>>>       uint32_t                ndcb0;
>>>       uint32_t                ndcb1;
>>>       uint32_t                ndcb2;
>>> -
>>> -     /* timing calcuted from setting */
>>> -     uint32_t                ndtr0cs0;
>>> -     uint32_t                ndtr1cs0;
>>> -
>>> -     /* calculated from pxa3xx_nand_flash data */
>>> -     size_t          oob_size;
>>> -     size_t          read_id_bytes;
>>> -
>>> -     unsigned int    col_addr_cycles;
>>> -     unsigned int    row_addr_cycles;
>>>  };
>> It looks like if you switch the names of the structures above,
>> then the patch will be much shorter and cleaner,
>> but will it make structures meaning confusion?
> Could you elaborate more about this? Do you means no need to create a seperated
> structure?

No, if you have 2 chips, then certainly you'd better have a separate
structure for the chip description.

What I was wondering, is that there are many places in the patch where
you change the "info" to "nand", may be this can be avoided if you keep
the structure that describes the controller ("pxa3xx_nand" in the patch)
named "pxa3xx_nand_info" and the new structure that describes the chip
will be named something else like pxa3xx_nand_chip or anything like this.
This way, your patch will not have to change the "info" to "nand" in many
places (or maybe even all places) and will be much shorter and will include
only functional changes.



[...]

>>>  static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>>>  {
>>> -     struct pxa3xx_nand_info *info = devid;
>>> -     unsigned int status, is_completed = 0;
>>> +     struct pxa3xx_nand *nand = devid;
>>> +     struct pxa3xx_nand_info *info;
>>> +     unsigned int status, is_completed = 0, cs;
>>> +     unsigned int ready, cmd_done, page_done, badblock_detect;
>>>
>>> -     status = nand_readl(info, NDSR);
>>> +     cs              = nand->chip_select;
>>> +     ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
>>> +     cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
>>> +     page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
>>> +     badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
>> This is confusing... do you use to ?: operator for differentiating between
>> cs = 0 and cs = 1?
>> I think this is a bad idea.
>> Moreover, the use of ?: is discouraged among the kernel.
> I could use if/else to replace this statement.

or switch, what ever you like, but please, don't leave it like
if (cs) ..., because this is confusing it has a meaning of "cs is good or bad?",
while actually, you want to know if it is 0 or 1 (both values are good).


[...]

>>> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>>>               uint16_t column, int page_addr)
>>>  {
>>>       uint16_t cmd;
>>>       int addr_cycle, exec_cmd, ndcb0;
>>> -     struct mtd_info *mtd = info->mtd;
>>> +     struct mtd_info *mtd;
>>> +     struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>>>
>>> -     ndcb0 = 0;
>>> +     mtd = get_mtd_by_info(info);
>>> +     ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
>> This one is confusing too...
>> Besides, you don't need the parenthesis.
> Got that. I would fix it.

Good, my previous explanation also applies here.
Thanks


[...]

>>>  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>>>  {
>>> -     struct mtd_info *mtd = info->mtd;
>>> +     struct pxa3xx_nand *nand = info->nand_data;
>>> +     struct mtd_info *mtd = get_mtd_by_info(info);
>>>       struct nand_chip *chip = mtd->priv;
>>>
>>>       /* use the common timing to make a try */
>>> -     pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
>>> +     if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
>>> +             return 0;
>>>       chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
>>> -     if (info->is_ready)
>>> +     if (nand->is_ready)
>>>               return 1;
>>>       else
>>>               return 0;
>> I think it is time to change this function return convention to propagate
>> errors and not just 0 or 1, (may be in separate patch) what do you think?
> How about return 0 when sensing the READY signal, or return -ENODEV?

Yes, this should be fine, but also don't forget about
pxa3xx_nand_config_flash() function call - it can return 0 or -EINVAL
and it should also propagate instead of being squashed.

>>> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>>>  static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>>  {
>>>       struct pxa3xx_nand_info *info = mtd->priv;
>>> -     struct platform_device *pdev = info->pdev;
>>> +     struct pxa3xx_nand *nand = info->nand_data;
>>> +     struct platform_device *pdev = nand->pdev;
>>>       struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>>       struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>>>       const struct pxa3xx_nand_flash *f = NULL;
>>> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>>       uint64_t chipsize;
>>>       int i, ret, num;
>>>
>>> +     nand->chip_select = info->chip_select;
>>>       if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>>>               goto KEEP_CONFIG;
>>>
>>>       ret = pxa3xx_nand_sensing(info);
>>>       if (!ret) {
>>> -             kfree(mtd);
>>> -             info->mtd = NULL;
>>> -             printk(KERN_INFO "There is no nand chip on cs 0!\n");
>>> +             free_cs_resource(info, nand->chip_select);
>>> +             printk(KERN_INFO "There is no nand chip on cs %d!\n",
>>> +                             nand->chip_select);
>>>
>>>               return -EINVAL;

after the change of pxa3xx_nand_sensing() function,
you should change the above "return -EINVAL;" into "return ret;"



[...]


-- 
Regards,
Igor.

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-23 10:44       ` Igor Grinberg
@ 2011-06-25 11:17         ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-25 11:17 UTC (permalink / raw)
  To: Igor Grinberg, Daniel Mack, Eric Miao, David Woodhouse,
	Artem Bityutskiy, Haojian Zhuang, linux-mtd, linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
 9 files changed, 346 insertions(+), 224 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..4e17309 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,10 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..0825b7d 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,40 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
 
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			cs;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
+
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,44 +162,27 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
+	struct pxa3xx_nand_host	*host[NUM_CHIP_SELECT];
 	/* relate to the command */
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
 
-	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -241,9 +244,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +262,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +297,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -359,8 +365,8 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
-				info->state);
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
+			info->state);
 		BUG();
 	}
 }
@@ -385,7 +391,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -416,9 +422,17 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
-
 	if (status & NDSR_DBERR)
 		info->retcode = ERR_DBERR;
 	if (status & NDSR_SBERR)
@@ -437,11 +451,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -463,12 +477,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +489,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -495,6 +503,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -512,20 +524,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +548,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +575,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +585,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +594,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +604,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +615,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -616,7 +627,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
+		dev_err(&info->pdev->dev, "pxa3xx-nand: non-supported"
 			" command %x\n", command);
 		break;
 	}
@@ -627,7 +638,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +647,21 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -646,12 +670,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +688,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +720,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +733,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +746,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +757,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +778,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,54 +802,69 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -829,59 +874,41 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
  * data buffer and the DMA descriptor
  */
 #define MAX_BUFF_SIZE	PAGE_SIZE
-
-static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
+static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
 {
-	struct platform_device *pdev = info->pdev;
-	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
-
-	if (use_dma == 0) {
-		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
-		if (info->data_buff == NULL)
-			return -ENOMEM;
-		return 0;
-	}
-
-	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
-				&info->data_buff_phys, GFP_KERNEL);
-	if (info->data_buff == NULL) {
-		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
-		return -ENOMEM;
-	}
-
-	info->data_buff_size = MAX_BUFF_SIZE;
-	info->data_desc = (void *)info->data_buff + data_desc_offset;
-	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
-	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
-				pxa3xx_nand_data_dma_irq, info);
-	if (info->data_dma_ch < 0) {
-		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-				info->data_buff, info->data_buff_phys);
-		return info->data_dma_ch;
-	}
+	if (!info->host[cs])
+		return;
 
-	return 0;
+	host = info->host[cs];
+	mtd = host->mtd;
+	kfree(mtd);
+	info->host[cs] = NULL;
 }
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host[info->cs]->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -891,26 +918,26 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	uint64_t chipsize;
 	int i, ret, num;
 
+	info->cs = host->cs;
 	if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+	if (ret) {
+		free_cs_resource(info, info->cs);
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		free_cs_resource(info, info->cs);
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+			 "potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -928,14 +955,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		free_cs_resource(info, info->cs);
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -950,66 +980,51 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
+		host->row_addr_cycles = 2;
+	mtd->name = mtd_names[info->cs];
 	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
+	chip->ecc.size = host->page_size;
 
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
+	chip->options = 0;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options = NAND_BUSWIDTH_16;
 	chip->options |= NAND_NO_AUTOINCR;
 	chip->options |= NAND_NO_READRDY;
 
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
+	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
-			GFP_KERNEL);
-	if (!mtd) {
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
-
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
-
-	spin_lock_init(&chip->controller->lock);
-	init_waitqueue_head(&chip->controller->wq);
 	info->clk = clk_get(&pdev->dev, NULL);
 	if (IS_ERR(info->clk)) {
 		dev_err(&pdev->dev, "failed to get nand clock\n");
 		ret = PTR_ERR(info->clk);
-		goto fail_free_mtd;
+		goto fail_alloc;
 	}
 	clk_enable(info->clk);
 
@@ -1058,10 +1073,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	}
 	info->mmio_phys = r->start;
 
-	ret = pxa3xx_nand_init_buff(info);
-	if (ret)
-		goto fail_free_io;
-
 	/* initialize all interrupts to be disabled */
 	disable_int(info, NDSR_MASK);
 
@@ -1069,21 +1080,80 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 			  pdev->name, info);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to request IRQ\n");
-		goto fail_free_buf;
+		ret = ENXIO;
+		goto fail_free_io;
 	}
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = kzalloc(sizeof(struct mtd_info)
+				+ sizeof(struct pxa3xx_nand_host),
+				GFP_KERNEL);
+		if (!mtd) {
+			dev_err(&pdev->dev, "failed to allocate memory\n");
+			ret = -ENOMEM;
+			goto fail_free_irq;
+		}
+
+		host = (struct pxa3xx_nand_host *)(&mtd[1]);
+		host->info_data = info;
+		host->cs = cs;
+		host->mtd = mtd;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+		info->host[cs] = host;
+
+		chip = (struct nand_chip *)(&mtd[1]);
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
+
+	if (use_dma == 0) {
+		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
+		if (info->data_buff == NULL) {
+			ret = -ENOMEM;
+			goto fail_free_buf;
+		}
+		goto success_exit;
+	}
+
+	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
+			&info->data_buff_phys, GFP_KERNEL);
+	if (info->data_buff == NULL) {
+		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
+		ret = -ENOMEM;
+		goto fail_free_buf;
+	}
+
+	info->data_desc = (void *)info->data_buff + data_desc_offset;
+	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
+				pxa3xx_nand_data_dma_irq, info);
+	if (info->data_dma_ch < 0) {
+		dev_err(&pdev->dev, "failed to request data dma\n");
+		ret = -ENXIO;
+		goto fail_free_dma_buf;
+	}
+success_exit:
+	return 0;
 
+fail_free_dma_buf:
+	dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
+			info->data_buff, info->data_buff_phys);
 fail_free_buf:
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		free_cs_resource(info, cs);
+fail_free_irq:
 	free_irq(irq, info);
-	if (use_dma) {
-		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-			info->data_buff, info->data_buff_phys);
-	} else
-		kfree(info->data_buff);
 fail_free_io:
 	iounmap(info->mmio_base);
 fail_free_res:
@@ -1091,18 +1161,20 @@ fail_free_res:
 fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
-fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+fail_alloc:
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand_platform_data *pdata;
+	struct pxa3xx_nand_host *host;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1110,7 +1182,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1122,9 +1194,12 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		host = info->host[cs];
+		if (!host)
+			continue;
+		nand_release(host->mtd);
+		free_cs_resource(info, cs);
 	}
 	return 0;
 }
@@ -1133,6 +1208,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int cs, ret, probe_success = 0;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1140,18 +1216,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	if (pdata->keep_config && pdata->num_cs > 1) {
+		dev_warn(&pdev->dev, "keep_config is prohibited with multiple"
+			 " chip selects feature!\n");
+		pdata->keep_config = 0;
+	}
+
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
+
+	info = platform_get_drvdata(pdev);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to scan nand\n");
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+			pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1171,8 +1267,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-25 11:17         ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-25 11:17 UTC (permalink / raw)
  To: linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
 9 files changed, 346 insertions(+), 224 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..4e17309 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,10 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..0825b7d 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,40 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
 
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			cs;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
+
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,44 +162,27 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
+	struct pxa3xx_nand_host	*host[NUM_CHIP_SELECT];
 	/* relate to the command */
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
 
-	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -241,9 +244,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +262,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +297,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -359,8 +365,8 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
-				info->state);
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
+			info->state);
 		BUG();
 	}
 }
@@ -385,7 +391,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -416,9 +422,17 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
-
 	if (status & NDSR_DBERR)
 		info->retcode = ERR_DBERR;
 	if (status & NDSR_SBERR)
@@ -437,11 +451,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -463,12 +477,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +489,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -495,6 +503,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -512,20 +524,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +548,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +575,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +585,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +594,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +604,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +615,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -616,7 +627,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
+		dev_err(&info->pdev->dev, "pxa3xx-nand: non-supported"
 			" command %x\n", command);
 		break;
 	}
@@ -627,7 +638,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +647,21 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -646,12 +670,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +688,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +720,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +733,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +746,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +757,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +778,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,54 +802,69 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -829,59 +874,41 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
  * data buffer and the DMA descriptor
  */
 #define MAX_BUFF_SIZE	PAGE_SIZE
-
-static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
+static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
 {
-	struct platform_device *pdev = info->pdev;
-	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
-
-	if (use_dma == 0) {
-		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
-		if (info->data_buff == NULL)
-			return -ENOMEM;
-		return 0;
-	}
-
-	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
-				&info->data_buff_phys, GFP_KERNEL);
-	if (info->data_buff == NULL) {
-		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
-		return -ENOMEM;
-	}
-
-	info->data_buff_size = MAX_BUFF_SIZE;
-	info->data_desc = (void *)info->data_buff + data_desc_offset;
-	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
-	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
-				pxa3xx_nand_data_dma_irq, info);
-	if (info->data_dma_ch < 0) {
-		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-				info->data_buff, info->data_buff_phys);
-		return info->data_dma_ch;
-	}
+	if (!info->host[cs])
+		return;
 
-	return 0;
+	host = info->host[cs];
+	mtd = host->mtd;
+	kfree(mtd);
+	info->host[cs] = NULL;
 }
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host[info->cs]->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -891,26 +918,26 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	uint64_t chipsize;
 	int i, ret, num;
 
+	info->cs = host->cs;
 	if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+	if (ret) {
+		free_cs_resource(info, info->cs);
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		free_cs_resource(info, info->cs);
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+			 "potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -928,14 +955,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		free_cs_resource(info, info->cs);
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -950,66 +980,51 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
+		host->row_addr_cycles = 2;
+	mtd->name = mtd_names[info->cs];
 	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
+	chip->ecc.size = host->page_size;
 
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
+	chip->options = 0;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options = NAND_BUSWIDTH_16;
 	chip->options |= NAND_NO_AUTOINCR;
 	chip->options |= NAND_NO_READRDY;
 
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
+	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
-			GFP_KERNEL);
-	if (!mtd) {
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
-
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
-
-	spin_lock_init(&chip->controller->lock);
-	init_waitqueue_head(&chip->controller->wq);
 	info->clk = clk_get(&pdev->dev, NULL);
 	if (IS_ERR(info->clk)) {
 		dev_err(&pdev->dev, "failed to get nand clock\n");
 		ret = PTR_ERR(info->clk);
-		goto fail_free_mtd;
+		goto fail_alloc;
 	}
 	clk_enable(info->clk);
 
@@ -1058,10 +1073,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	}
 	info->mmio_phys = r->start;
 
-	ret = pxa3xx_nand_init_buff(info);
-	if (ret)
-		goto fail_free_io;
-
 	/* initialize all interrupts to be disabled */
 	disable_int(info, NDSR_MASK);
 
@@ -1069,21 +1080,80 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 			  pdev->name, info);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to request IRQ\n");
-		goto fail_free_buf;
+		ret = ENXIO;
+		goto fail_free_io;
 	}
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = kzalloc(sizeof(struct mtd_info)
+				+ sizeof(struct pxa3xx_nand_host),
+				GFP_KERNEL);
+		if (!mtd) {
+			dev_err(&pdev->dev, "failed to allocate memory\n");
+			ret = -ENOMEM;
+			goto fail_free_irq;
+		}
+
+		host = (struct pxa3xx_nand_host *)(&mtd[1]);
+		host->info_data = info;
+		host->cs = cs;
+		host->mtd = mtd;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+		info->host[cs] = host;
+
+		chip = (struct nand_chip *)(&mtd[1]);
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
+
+	if (use_dma == 0) {
+		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
+		if (info->data_buff == NULL) {
+			ret = -ENOMEM;
+			goto fail_free_buf;
+		}
+		goto success_exit;
+	}
+
+	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
+			&info->data_buff_phys, GFP_KERNEL);
+	if (info->data_buff == NULL) {
+		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
+		ret = -ENOMEM;
+		goto fail_free_buf;
+	}
+
+	info->data_desc = (void *)info->data_buff + data_desc_offset;
+	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
+				pxa3xx_nand_data_dma_irq, info);
+	if (info->data_dma_ch < 0) {
+		dev_err(&pdev->dev, "failed to request data dma\n");
+		ret = -ENXIO;
+		goto fail_free_dma_buf;
+	}
+success_exit:
+	return 0;
 
+fail_free_dma_buf:
+	dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
+			info->data_buff, info->data_buff_phys);
 fail_free_buf:
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		free_cs_resource(info, cs);
+fail_free_irq:
 	free_irq(irq, info);
-	if (use_dma) {
-		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-			info->data_buff, info->data_buff_phys);
-	} else
-		kfree(info->data_buff);
 fail_free_io:
 	iounmap(info->mmio_base);
 fail_free_res:
@@ -1091,18 +1161,20 @@ fail_free_res:
 fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
-fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+fail_alloc:
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand_platform_data *pdata;
+	struct pxa3xx_nand_host *host;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1110,7 +1182,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1122,9 +1194,12 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		host = info->host[cs];
+		if (!host)
+			continue;
+		nand_release(host->mtd);
+		free_cs_resource(info, cs);
 	}
 	return 0;
 }
@@ -1133,6 +1208,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int cs, ret, probe_success = 0;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1140,18 +1216,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	if (pdata->keep_config && pdata->num_cs > 1) {
+		dev_warn(&pdev->dev, "keep_config is prohibited with multiple"
+			 " chip selects feature!\n");
+		pdata->keep_config = 0;
+	}
+
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
+
+	info = platform_get_drvdata(pdev);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to scan nand\n");
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+			pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1171,8 +1267,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-25 11:17         ` Lei Wen
@ 2011-06-25 12:32           ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-06-25 12:32 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Haojian Zhuang,
	linux-mtd, Igor Grinberg, linux-arm-kernel

On Sat, Jun 25, 2011 at 1:17 PM, Lei Wen <leiwen@marvell.com> wrote:
> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
>
> Update platform driver to support this feature.
>
> Another 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.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>

I tested this on a PXA303 platform with one chipselect only, at at
least I can say that this patch doesn't seem to cause any regression
here. But I couldn't test the new feature it adds.

So, FWIW:

Tested-by: Daniel Mack <zonque@gmail.com>


Thanks!
Daniel


> ---
>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
>  9 files changed, 346 insertions(+), 224 deletions(-)
>
> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
> index 06b5fa8..c4996f3 100644
> --- a/arch/arm/mach-mmp/aspenite.c
> +++ b/arch/arm/mach-mmp/aspenite.c
> @@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
>
>  static struct pxa3xx_nand_platform_data aspenite_nand_info = {
>        .enable_arbiter = 1,
> -       .parts          = aspenite_nand_partitions,
> -       .nr_parts       = ARRAY_SIZE(aspenite_nand_partitions),
> +       .num_cs = 1,
> +       .parts[0]       = aspenite_nand_partitions,
> +       .nr_parts[0]    = ARRAY_SIZE(aspenite_nand_partitions),
>  };
>
>  static struct i2c_board_info aspenite_i2c_info[] __initdata = {
> diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
> index b2248e7..969d252 100644
> --- a/arch/arm/mach-pxa/cm-x300.c
> +++ b/arch/arm/mach-pxa/cm-x300.c
> @@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
>        .enable_arbiter = 1,
>        .keep_config    = 1,
> -       .parts          = cm_x300_nand_partitions,
> -       .nr_parts       = ARRAY_SIZE(cm_x300_nand_partitions),
> +       .num_cs         = 1,
> +       .parts[0]       = cm_x300_nand_partitions,
> +       .nr_parts[0]    = ARRAY_SIZE(cm_x300_nand_partitions),
>  };
>
>  static void __init cm_x300_init_nand(void)
> diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
> index 3f9be41..2b8ca0d 100644
> --- a/arch/arm/mach-pxa/colibri-pxa3xx.c
> +++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
> @@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data colibri_nand_info = {
>        .enable_arbiter = 1,
>        .keep_config    = 1,
> -       .parts          = colibri_nand_partitions,
> -       .nr_parts       = ARRAY_SIZE(colibri_nand_partitions),
> +       .num_cs         = 1,
> +       .parts[0]       = colibri_nand_partitions,
> +       .nr_parts[0]    = ARRAY_SIZE(colibri_nand_partitions),
>  };
>
>  void __init colibri_pxa3xx_init_nand(void)
> diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
> index e5e326d..f6d1f9d 100644
> --- a/arch/arm/mach-pxa/littleton.c
> +++ b/arch/arm/mach-pxa/littleton.c
> @@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
>
>  static struct pxa3xx_nand_platform_data littleton_nand_info = {
>        .enable_arbiter = 1,
> -       .parts          = littleton_nand_partitions,
> -       .nr_parts       = ARRAY_SIZE(littleton_nand_partitions),
> +       .num_cs         = 1,
> +       .parts[0]       = littleton_nand_partitions,
> +       .nr_parts[0]    = ARRAY_SIZE(littleton_nand_partitions),
>  };
>
>  static void __init littleton_init_nand(void)
> diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
> index b5a8fd3..90928d6 100644
> --- a/arch/arm/mach-pxa/mxm8x10.c
> +++ b/arch/arm/mach-pxa/mxm8x10.c
> @@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
>  };
>
>  static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
> -       .enable_arbiter = 1,
> -       .keep_config = 1,
> -       .parts = mxm_8x10_nand_partitions,
> -       .nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
> +       .enable_arbiter = 1,
> +       .keep_config    = 1,
> +       .num_cs         = 1,
> +       .parts[0]       = mxm_8x10_nand_partitions,
> +       .nr_parts[0]    = ARRAY_SIZE(mxm_8x10_nand_partitions)
>  };
>
>  static void __init mxm_8x10_nand_init(void)
> diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
> index d130f77..c7ec847 100644
> --- a/arch/arm/mach-pxa/raumfeld.c
> +++ b/arch/arm/mach-pxa/raumfeld.c
> @@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
>        .enable_arbiter = 1,
>        .keep_config    = 1,
> -       .parts          = raumfeld_nand_partitions,
> -       .nr_parts       = ARRAY_SIZE(raumfeld_nand_partitions),
> +       .num_cs         = 1,
> +       .parts[0]       = raumfeld_nand_partitions,
> +       .nr_parts[0]    = ARRAY_SIZE(raumfeld_nand_partitions),
>  };
>
>  /**
> diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
> index 5821185..64fdac9 100644
> --- a/arch/arm/mach-pxa/zylonite.c
> +++ b/arch/arm/mach-pxa/zylonite.c
> @@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
>
>  static struct pxa3xx_nand_platform_data zylonite_nand_info = {
>        .enable_arbiter = 1,
> -       .parts          = zylonite_nand_partitions,
> -       .nr_parts       = ARRAY_SIZE(zylonite_nand_partitions),
> +       .num_cs         = 1,
> +       .parts[0]       = zylonite_nand_partitions,
> +       .nr_parts[0]    = ARRAY_SIZE(zylonite_nand_partitions),
>  };
>
>  static void __init zylonite_init_nand(void)
> diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> index 442301f..4e17309 100644
> --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> @@ -41,6 +41,19 @@ struct pxa3xx_nand_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
> @@ -52,8 +65,10 @@ struct pxa3xx_nand_platform_data {
>        /* allow platform code to keep OBM/bootloader defined NFC config */
>        int     keep_config;
>
> -       const struct mtd_partition              *parts;
> -       unsigned int                            nr_parts;
> +       /* indicate how many chip selects will be used */
> +       int     num_cs;
> +       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;
> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index b7db1b2..0825b7d 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -110,6 +110,7 @@ enum {
>
>  enum {
>        STATE_IDLE = 0,
> +       STATE_PREPARED,
>        STATE_CMD_HANDLE,
>        STATE_DMA_READING,
>        STATE_DMA_WRITING,
> @@ -120,21 +121,40 @@ enum {
>        STATE_READY,
>  };
>
> -struct pxa3xx_nand_info {
> -       struct nand_chip        nand_chip;
> +struct pxa3xx_nand_host {
> +       struct nand_chip        chip;
> +       struct pxa3xx_nand_cmdset *cmdset;
> +       struct mtd_info         *mtd;
> +       void                    *info_data;
>
> +       /* page size of attached chip */
> +       unsigned int            page_size;
> +       int                     cs;
> +       int                     use_ecc;
> +
> +       /* calculated from pxa3xx_nand_flash data */
> +       unsigned int            col_addr_cycles;
> +       unsigned int            row_addr_cycles;
> +       size_t                  read_id_bytes;
> +
> +       /* cached register value */
> +       uint32_t                reg_ndcr;
> +       uint32_t                ndtr0cs0;
> +       uint32_t                ndtr1cs0;
> +};
> +
> +struct pxa3xx_nand_info {
>        struct nand_hw_control  controller;
>        struct platform_device   *pdev;
> -       struct pxa3xx_nand_cmdset *cmdset;
>
>        struct clk              *clk;
>        void __iomem            *mmio_base;
>        unsigned long           mmio_phys;
> +       struct completion       cmd_complete;
>
>        unsigned int            buf_start;
>        unsigned int            buf_count;
>
> -       struct mtd_info         *mtd;
>        /* DMA information */
>        int                     drcmr_dat;
>        int                     drcmr_cmd;
> @@ -142,44 +162,27 @@ struct pxa3xx_nand_info {
>        unsigned char           *data_buff;
>        unsigned char           *oob_buff;
>        dma_addr_t              data_buff_phys;
> -       size_t                  data_buff_size;
>        int                     data_dma_ch;
>        struct pxa_dma_desc     *data_desc;
>        dma_addr_t              data_desc_addr;
>
> -       uint32_t                reg_ndcr;
> -
> -       /* saved column/page_addr during CMD_SEQIN */
> -       int                     seqin_column;
> -       int                     seqin_page_addr;
> -
> +       struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
>        /* relate to the command */
>        unsigned int            state;
>
> +       int                     cs;
>        int                     use_ecc;        /* use HW ECC ? */
>        int                     use_dma;        /* use DMA ? */
>        int                     is_ready;
>
> -       unsigned int            page_size;      /* page size of attached chip */
>        unsigned int            data_size;      /* data size in FIFO */
> +       unsigned int            oob_size;
>        int                     retcode;
> -       struct completion       cmd_complete;
>
>        /* generated NDCBx register values */
>        uint32_t                ndcb0;
>        uint32_t                ndcb1;
>        uint32_t                ndcb2;
> -
> -       /* timing calcuted from setting */
> -       uint32_t                ndtr0cs0;
> -       uint32_t                ndtr1cs0;
> -
> -       /* calculated from pxa3xx_nand_flash data */
> -       size_t          oob_size;
> -       size_t          read_id_bytes;
> -
> -       unsigned int    col_addr_cycles;
> -       unsigned int    row_addr_cycles;
>  };
>
>  static int use_dma = 1;
> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>  /* Define a default flash type setting serve as flash detecting only */
>  #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>
> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>
>  #define NDTR0_tCH(c)   (min((c), 7) << 19)
>  #define NDTR0_tCS(c)   (min((c), 7) << 16)
> @@ -241,9 +244,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>  /* convert nano-seconds to nand flash controller clock cycles */
>  #define ns2cycle(ns, clk)      (int)((ns) * (clk / 1000000) / 1000)
>
> -static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
> +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
>                                   const struct pxa3xx_nand_timing *t)
>  {
> +       struct pxa3xx_nand_info *info = host->info_data;
>        unsigned long nand_clk = clk_get_rate(info->clk);
>        uint32_t ndtr0, ndtr1;
>
> @@ -258,23 +262,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>                NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
>                NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
>
> -       info->ndtr0cs0 = ndtr0;
> -       info->ndtr1cs0 = ndtr1;
> +       host->ndtr0cs0 = ndtr0;
> +       host->ndtr1cs0 = ndtr1;
>        nand_writel(info, NDTR0CS0, ndtr0);
>        nand_writel(info, NDTR1CS0, ndtr1);
>  }
>
>  static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>  {
> -       int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
> +       struct pxa3xx_nand_host *host = info->host[info->cs];
> +       int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
>
> -       info->data_size = info->page_size;
> +       info->data_size = host->page_size;
>        if (!oob_enable) {
>                info->oob_size = 0;
>                return;
>        }
>
> -       switch (info->page_size) {
> +       switch (host->page_size) {
>        case 2048:
>                info->oob_size = (info->use_ecc) ? 40 : 64;
>                break;
> @@ -292,9 +297,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>  */
>  static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
>  {
> +       struct pxa3xx_nand_host *host = info->host[info->cs];
>        uint32_t ndcr;
>
> -       ndcr = info->reg_ndcr;
> +       ndcr = host->reg_ndcr;
>        ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
>        ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
>        ndcr |= NDCR_ND_RUN;
> @@ -359,8 +365,8 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
>                                        DIV_ROUND_UP(info->oob_size, 4));
>                break;
>        default:
> -               printk(KERN_ERR "%s: invalid state %d\n", __func__,
> -                               info->state);
> +               dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
> +                       info->state);
>                BUG();
>        }
>  }
> @@ -385,7 +391,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
>                desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
>                break;
>        default:
> -               printk(KERN_ERR "%s: invalid state %d\n", __func__,
> +               dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
>                                info->state);
>                BUG();
>        }
> @@ -416,9 +422,17 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>  {
>        struct pxa3xx_nand_info *info = devid;
>        unsigned int status, is_completed = 0;
> +       unsigned int ready, cmd_done;
> +
> +       if (info->cs == 0) {
> +               ready           = NDSR_FLASH_RDY;
> +               cmd_done        = NDSR_CS0_CMDD;
> +       } else {
> +               ready           = NDSR_RDY;
> +               cmd_done        = NDSR_CS1_CMDD;
> +       }
>
>        status = nand_readl(info, NDSR);
> -
>        if (status & NDSR_DBERR)
>                info->retcode = ERR_DBERR;
>        if (status & NDSR_SBERR)
> @@ -437,11 +451,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>                        handle_data_pio(info);
>                }
>        }
> -       if (status & NDSR_CS0_CMDD) {
> +       if (status & cmd_done) {
>                info->state = STATE_CMD_DONE;
>                is_completed = 1;
>        }
> -       if (status & NDSR_FLASH_RDY) {
> +       if (status & ready) {
>                info->is_ready = 1;
>                info->state = STATE_READY;
>        }
> @@ -463,12 +477,6 @@ NORMAL_IRQ_EXIT:
>        return IRQ_HANDLED;
>  }
>
> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
> -{
> -       struct pxa3xx_nand_info *info = mtd->priv;
> -       return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
> -}
> -
>  static inline int is_buf_blank(uint8_t *buf, size_t len)
>  {
>        for (; len > 0; len--)
> @@ -481,10 +489,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>                uint16_t column, int page_addr)
>  {
>        uint16_t cmd;
> -       int addr_cycle, exec_cmd, ndcb0;
> -       struct mtd_info *mtd = info->mtd;
> +       int addr_cycle, exec_cmd;
> +       struct pxa3xx_nand_host *host = info->host[info->cs];
> +       struct mtd_info *mtd = host->mtd;
>
> -       ndcb0 = 0;
>        addr_cycle = 0;
>        exec_cmd = 1;
>
> @@ -495,6 +503,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>        info->use_ecc           = 0;
>        info->is_ready          = 0;
>        info->retcode           = ERR_NONE;
> +       if (info->cs != 0)
> +               info->ndcb0 = NDCB0_CSEL;
> +       else
> +               info->ndcb0 = 0;
>
>        switch (command) {
>        case NAND_CMD_READ0:
> @@ -512,20 +524,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>                break;
>        }
>
> -       info->ndcb0 = ndcb0;
> -       addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
> -                                   + info->col_addr_cycles);
> +       addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
> +                                   + host->col_addr_cycles);
>
>        switch (command) {
>        case NAND_CMD_READOOB:
>        case NAND_CMD_READ0:
> -               cmd = info->cmdset->read1;
> +               cmd = host->cmdset->read1;
>                if (command == NAND_CMD_READOOB)
>                        info->buf_start = mtd->writesize + column;
>                else
>                        info->buf_start = column;
>
> -               if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
> +               if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
>                        info->ndcb0 |= NDCB0_CMD_TYPE(0)
>                                        | addr_cycle
>                                        | (cmd & NDCB0_CMD1_MASK);
> @@ -537,7 +548,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>
>        case NAND_CMD_SEQIN:
>                /* small page addr setting */
> -               if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
> +               if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
>                        info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>                                        | (column & 0xFF);
>
> @@ -564,7 +575,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>                        break;
>                }
>
> -               cmd = info->cmdset->program;
> +               cmd = host->cmdset->program;
>                info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>                                | NDCB0_AUTO_RS
>                                | NDCB0_ST_ROW_EN
> @@ -574,8 +585,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>                break;
>
>        case NAND_CMD_READID:
> -               cmd = info->cmdset->read_id;
> -               info->buf_count = info->read_id_bytes;
> +               cmd = host->cmdset->read_id;
> +               info->buf_count = host->read_id_bytes;
>                info->ndcb0 |= NDCB0_CMD_TYPE(3)
>                                | NDCB0_ADDR_CYC(1)
>                                | cmd;
> @@ -583,7 +594,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>                info->data_size = 8;
>                break;
>        case NAND_CMD_STATUS:
> -               cmd = info->cmdset->read_status;
> +               cmd = host->cmdset->read_status;
>                info->buf_count = 1;
>                info->ndcb0 |= NDCB0_CMD_TYPE(4)
>                                | NDCB0_ADDR_CYC(1)
> @@ -593,7 +604,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>                break;
>
>        case NAND_CMD_ERASE1:
> -               cmd = info->cmdset->erase;
> +               cmd = host->cmdset->erase;
>                info->ndcb0 |= NDCB0_CMD_TYPE(2)
>                                | NDCB0_AUTO_RS
>                                | NDCB0_ADDR_CYC(3)
> @@ -604,7 +615,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>
>                break;
>        case NAND_CMD_RESET:
> -               cmd = info->cmdset->reset;
> +               cmd = host->cmdset->reset;
>                info->ndcb0 |= NDCB0_CMD_TYPE(5)
>                                | cmd;
>
> @@ -616,7 +627,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>
>        default:
>                exec_cmd = 0;
> -               printk(KERN_ERR "pxa3xx-nand: non-supported"
> +               dev_err(&info->pdev->dev, "pxa3xx-nand: non-supported"
>                        " command %x\n", command);
>                break;
>        }
> @@ -627,7 +638,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>                                int column, int page_addr)
>  {
> -       struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand_host *host = mtd->priv;
> +       struct pxa3xx_nand_info *info = host->info_data;
>        int ret, exec_cmd;
>
>        /*
> @@ -635,9 +647,21 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>         * "byte" address into a "word" address appropriate
>         * for indexing a word-oriented device
>         */
> -       if (info->reg_ndcr & NDCR_DWIDTH_M)
> +       if (host->reg_ndcr & NDCR_DWIDTH_M)
>                column /= 2;
>
> +       /*
> +        * There may be different NAND chip hooked to
> +        * different chip select, so check whether
> +        * chip select has been changed, if yes, reset the timing
> +        */
> +       if (info->cs != host->cs) {
> +               info->cs = host->cs;
> +               nand_writel(info, NDTR0CS0, host->ndtr0cs0);
> +               nand_writel(info, NDTR1CS0, host->ndtr1cs0);
> +       }
> +
> +       info->state = STATE_PREPARED;
>        exec_cmd = prepare_command_pool(info, command, column, page_addr);
>        if (exec_cmd) {
>                init_completion(&info->cmd_complete);
> @@ -646,12 +670,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>                ret = wait_for_completion_timeout(&info->cmd_complete,
>                                CHIP_DELAY_TIMEOUT);
>                if (!ret) {
> -                       printk(KERN_ERR "Wait time out!!!\n");
> +                       dev_err(&info->pdev->dev, "Wait time out!!!\n");
>                        /* Stop State Machine for next command cycle */
>                        pxa3xx_nand_stop(info);
>                }
> -               info->state = STATE_IDLE;
>        }
> +       info->state = STATE_IDLE;
>  }
>
>  static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
> @@ -664,7 +688,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
>  static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>                struct nand_chip *chip, uint8_t *buf, int page)
>  {
> -       struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand_host *host = mtd->priv;
> +       struct pxa3xx_nand_info *info = host->info_data;
>
>        chip->read_buf(mtd, buf, mtd->writesize);
>        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
> @@ -695,7 +720,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>
>  static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>  {
> -       struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand_host *host = mtd->priv;
> +       struct pxa3xx_nand_info *info = host->info_data;
>        char retval = 0xFF;
>
>        if (info->buf_start < info->buf_count)
> @@ -707,7 +733,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>
>  static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>  {
> -       struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand_host *host = mtd->priv;
> +       struct pxa3xx_nand_info *info = host->info_data;
>        u16 retval = 0xFFFF;
>
>        if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
> @@ -719,7 +746,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>
>  static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>  {
> -       struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand_host *host = mtd->priv;
> +       struct pxa3xx_nand_info *info = host->info_data;
>        int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>
>        memcpy(buf, info->data_buff + info->buf_start, real_len);
> @@ -729,7 +757,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>  static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
>                const uint8_t *buf, int len)
>  {
> -       struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand_host *host = mtd->priv;
> +       struct pxa3xx_nand_info *info = host->info_data;
>        int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>
>        memcpy(info->data_buff + info->buf_start, buf, real_len);
> @@ -749,7 +778,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>
>  static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>  {
> -       struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand_host *host = mtd->priv;
> +       struct pxa3xx_nand_info *info = host->info_data;
>
>        /* pxa3xx_nand_send_command has waited for command complete */
>        if (this->state == FL_WRITING || this->state == FL_ERASING) {
> @@ -772,54 +802,69 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>  {
>        struct platform_device *pdev = info->pdev;
>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
> +       struct pxa3xx_nand_host *host = info->host[info->cs];
>        uint32_t ndcr = 0x0; /* enable all interrupts */
>
> -       if (f->page_size != 2048 && f->page_size != 512)
> +       if (f->page_size != 2048 && f->page_size != 512) {
> +               dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
>                return -EINVAL;
> +       }
>
> -       if (f->flash_width != 16 && f->flash_width != 8)
> +       if (f->flash_width != 16 && f->flash_width != 8) {
> +               dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
>                return -EINVAL;
> +       }
>
>        /* calculate flash information */
> -       info->cmdset = &default_cmdset;
> -       info->page_size = f->page_size;
> -       info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
> +       host->cmdset = &default_cmdset;
> +       host->page_size = f->page_size;
> +       host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
>
>        /* calculate addressing information */
> -       info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
> +       host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
>
>        if (f->num_blocks * f->page_per_block > 65536)
> -               info->row_addr_cycles = 3;
> +               host->row_addr_cycles = 3;
>        else
> -               info->row_addr_cycles = 2;
> +               host->row_addr_cycles = 2;
>
>        ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
> -       ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
> +       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(info->read_id_bytes);
> +       ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
>        ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>
> -       info->reg_ndcr = ndcr;
> +       host->reg_ndcr = ndcr;
>
> -       pxa3xx_nand_set_timing(info, f->timing);
> +       pxa3xx_nand_set_timing(host, f->timing);
>        return 0;
>  }
>
>  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>  {
> +       /*
> +        * 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
> +        */
> +       struct pxa3xx_nand_host *host = info->host[0];
>        uint32_t ndcr = nand_readl(info, NDCR);
> -       info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
> -       /* set info fields needed to read id */
> -       info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
> -       info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> -       info->cmdset = &default_cmdset;
>
> -       info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> -       info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
> +       if (ndcr & NDCR_PAGE_SZ) {
> +               host->page_size = 2048;
> +               host->read_id_bytes = 4;
> +       } else {
> +               host->page_size = 512;
> +               host->read_id_bytes = 2;
> +       }
> +       host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> +       host->cmdset = &default_cmdset;
> +
> +       host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> +       host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
>
>        return 0;
>  }
> @@ -829,59 +874,41 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>  * data buffer and the DMA descriptor
>  */
>  #define MAX_BUFF_SIZE  PAGE_SIZE
> -
> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
>  {
> -       struct platform_device *pdev = info->pdev;
> -       int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
> -
> -       if (use_dma == 0) {
> -               info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> -               if (info->data_buff == NULL)
> -                       return -ENOMEM;
> -               return 0;
> -       }
> -
> -       info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> -                               &info->data_buff_phys, GFP_KERNEL);
> -       if (info->data_buff == NULL) {
> -               dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> -               return -ENOMEM;
> -       }
> -
> -       info->data_buff_size = MAX_BUFF_SIZE;
> -       info->data_desc = (void *)info->data_buff + data_desc_offset;
> -       info->data_desc_addr = info->data_buff_phys + data_desc_offset;
> +       struct pxa3xx_nand_host *host;
> +       struct mtd_info *mtd;
>
> -       info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> -                               pxa3xx_nand_data_dma_irq, info);
> -       if (info->data_dma_ch < 0) {
> -               dev_err(&pdev->dev, "failed to request data dma\n");
> -               dma_free_coherent(&pdev->dev, info->data_buff_size,
> -                               info->data_buff, info->data_buff_phys);
> -               return info->data_dma_ch;
> -       }
> +       if (!info->host[cs])
> +               return;
>
> -       return 0;
> +       host = info->host[cs];
> +       mtd = host->mtd;
> +       kfree(mtd);
> +       info->host[cs] = NULL;
>  }
>
>  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>  {
> -       struct mtd_info *mtd = info->mtd;
> -       struct nand_chip *chip = mtd->priv;
> +       struct mtd_info *mtd = info->host[info->cs]->mtd;
> +       int ret;
>
>        /* use the common timing to make a try */
> -       pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
> -       chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
> +       ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
> +       if (ret)
> +               return ret;
> +
> +       pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
>        if (info->is_ready)
> -               return 1;
> -       else
>                return 0;
> +
> +       return -ENODEV;
>  }
>
>  static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  {
> -       struct pxa3xx_nand_info *info = mtd->priv;
> +       struct pxa3xx_nand_host *host = mtd->priv;
> +       struct pxa3xx_nand_info *info = host->info_data;
>        struct platform_device *pdev = info->pdev;
>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>        struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
> @@ -891,26 +918,26 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>        uint64_t chipsize;
>        int i, ret, num;
>
> +       info->cs = host->cs;
>        if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>                goto KEEP_CONFIG;
>
>        ret = pxa3xx_nand_sensing(info);
> -       if (!ret) {
> -               kfree(mtd);
> -               info->mtd = NULL;
> -               printk(KERN_INFO "There is no nand chip on cs 0!\n");
> +       if (ret) {
> +               free_cs_resource(info, info->cs);
> +               dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
>
> -               return -EINVAL;
> +               return ret;
>        }
>
>        chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>        id = *((uint16_t *)(info->data_buff));
>        if (id != 0)
> -               printk(KERN_INFO "Detect a flash id %x\n", id);
> +               dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
>        else {
> -               kfree(mtd);
> -               info->mtd = NULL;
> -               printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
> +               free_cs_resource(info, info->cs);
> +               dev_warn(&info->pdev->dev, "Read out ID 0, "
> +                        "potential timing set wrong!!\n");
>
>                return -EINVAL;
>        }
> @@ -928,14 +955,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>        }
>
>        if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
> -               kfree(mtd);
> -               info->mtd = NULL;
> -               printk(KERN_ERR "ERROR!! flash not defined!!!\n");
> +               free_cs_resource(info, info->cs);
> +               dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
>
>                return -EINVAL;
>        }
>
> -       pxa3xx_nand_config_flash(info, f);
> +       ret = pxa3xx_nand_config_flash(info, f);
> +       if (ret) {
> +               dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
> +               return ret;
> +       }
>        pxa3xx_flash_ids[0].name = f->name;
>        pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
>        pxa3xx_flash_ids[0].pagesize = f->page_size;
> @@ -950,66 +980,51 @@ KEEP_CONFIG:
>        if (nand_scan_ident(mtd, 1, def))
>                return -ENODEV;
>        /* calculate addressing information */
> -       info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
> +       if (mtd->writesize >= 2048)
> +               host->col_addr_cycles = 2;
> +       else
> +               host->col_addr_cycles = 1;
>        info->oob_buff = info->data_buff + mtd->writesize;
>        if ((mtd->size >> chip->page_shift) > 65536)
> -               info->row_addr_cycles = 3;
> +               host->row_addr_cycles = 3;
>        else
> -               info->row_addr_cycles = 2;
> -       mtd->name = mtd_names[0];
> +               host->row_addr_cycles = 2;
> +       mtd->name = mtd_names[info->cs];
>        chip->ecc.mode = NAND_ECC_HW;
> -       chip->ecc.size = info->page_size;
> +       chip->ecc.size = host->page_size;
>
> -       chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
> +       chip->options = 0;
> +       if (host->reg_ndcr & NDCR_DWIDTH_M)
> +               chip->options = NAND_BUSWIDTH_16;
>        chip->options |= NAND_NO_AUTOINCR;
>        chip->options |= NAND_NO_READRDY;
>
>        return nand_scan_tail(mtd);
>  }
>
> -static
> -struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
> +static int alloc_nand_resource(struct platform_device *pdev)
>  {
> +       struct pxa3xx_nand_platform_data *pdata;
>        struct pxa3xx_nand_info *info;
> +       struct pxa3xx_nand_host *host;
>        struct nand_chip *chip;
>        struct mtd_info *mtd;
>        struct resource *r;
> -       int ret, irq;
> +       int ret, irq, cs;
> +       int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>
> -       mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
> -                       GFP_KERNEL);
> -       if (!mtd) {
> +       pdata = pdev->dev.platform_data;
> +       info = kzalloc(sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
> +       if (!info) {
>                dev_err(&pdev->dev, "failed to allocate memory\n");
> -               return NULL;
> +               return -ENOMEM;
>        }
> -
> -       info = (struct pxa3xx_nand_info *)(&mtd[1]);
> -       chip = (struct nand_chip *)(&mtd[1]);
>        info->pdev = pdev;
> -       info->mtd = mtd;
> -       mtd->priv = info;
> -       mtd->owner = THIS_MODULE;
> -
> -       chip->ecc.read_page     = pxa3xx_nand_read_page_hwecc;
> -       chip->ecc.write_page    = pxa3xx_nand_write_page_hwecc;
> -       chip->controller        = &info->controller;
> -       chip->waitfunc          = pxa3xx_nand_waitfunc;
> -       chip->select_chip       = pxa3xx_nand_select_chip;
> -       chip->dev_ready         = pxa3xx_nand_dev_ready;
> -       chip->cmdfunc           = pxa3xx_nand_cmdfunc;
> -       chip->read_word         = pxa3xx_nand_read_word;
> -       chip->read_byte         = pxa3xx_nand_read_byte;
> -       chip->read_buf          = pxa3xx_nand_read_buf;
> -       chip->write_buf         = pxa3xx_nand_write_buf;
> -       chip->verify_buf        = pxa3xx_nand_verify_buf;
> -
> -       spin_lock_init(&chip->controller->lock);
> -       init_waitqueue_head(&chip->controller->wq);
>        info->clk = clk_get(&pdev->dev, NULL);
>        if (IS_ERR(info->clk)) {
>                dev_err(&pdev->dev, "failed to get nand clock\n");
>                ret = PTR_ERR(info->clk);
> -               goto fail_free_mtd;
> +               goto fail_alloc;
>        }
>        clk_enable(info->clk);
>
> @@ -1058,10 +1073,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>        }
>        info->mmio_phys = r->start;
>
> -       ret = pxa3xx_nand_init_buff(info);
> -       if (ret)
> -               goto fail_free_io;
> -
>        /* initialize all interrupts to be disabled */
>        disable_int(info, NDSR_MASK);
>
> @@ -1069,21 +1080,80 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>                          pdev->name, info);
>        if (ret < 0) {
>                dev_err(&pdev->dev, "failed to request IRQ\n");
> -               goto fail_free_buf;
> +               ret = ENXIO;
> +               goto fail_free_io;
>        }
>
>        platform_set_drvdata(pdev, info);
>
> -       return info;
> +       for (cs = 0; cs < pdata->num_cs; cs++) {
> +               mtd = kzalloc(sizeof(struct mtd_info)
> +                               + sizeof(struct pxa3xx_nand_host),
> +                               GFP_KERNEL);
> +               if (!mtd) {
> +                       dev_err(&pdev->dev, "failed to allocate memory\n");
> +                       ret = -ENOMEM;
> +                       goto fail_free_irq;
> +               }
> +
> +               host = (struct pxa3xx_nand_host *)(&mtd[1]);
> +               host->info_data = info;
> +               host->cs = cs;
> +               host->mtd = mtd;
> +               mtd->priv = host;
> +               mtd->owner = THIS_MODULE;
> +               info->host[cs] = host;
> +
> +               chip = (struct nand_chip *)(&mtd[1]);
> +               chip->ecc.read_page     = pxa3xx_nand_read_page_hwecc;
> +               chip->ecc.write_page    = pxa3xx_nand_write_page_hwecc;
> +               chip->waitfunc          = pxa3xx_nand_waitfunc;
> +               chip->select_chip       = pxa3xx_nand_select_chip;
> +               chip->cmdfunc           = pxa3xx_nand_cmdfunc;
> +               chip->read_word         = pxa3xx_nand_read_word;
> +               chip->read_byte         = pxa3xx_nand_read_byte;
> +               chip->read_buf          = pxa3xx_nand_read_buf;
> +               chip->write_buf         = pxa3xx_nand_write_buf;
> +               chip->verify_buf        = pxa3xx_nand_verify_buf;
> +       }
> +
> +       if (use_dma == 0) {
> +               info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> +               if (info->data_buff == NULL) {
> +                       ret = -ENOMEM;
> +                       goto fail_free_buf;
> +               }
> +               goto success_exit;
> +       }
> +
> +       info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> +                       &info->data_buff_phys, GFP_KERNEL);
> +       if (info->data_buff == NULL) {
> +               dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> +               ret = -ENOMEM;
> +               goto fail_free_buf;
> +       }
> +
> +       info->data_desc = (void *)info->data_buff + data_desc_offset;
> +       info->data_desc_addr = info->data_buff_phys + data_desc_offset;
> +       info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> +                               pxa3xx_nand_data_dma_irq, info);
> +       if (info->data_dma_ch < 0) {
> +               dev_err(&pdev->dev, "failed to request data dma\n");
> +               ret = -ENXIO;
> +               goto fail_free_dma_buf;
> +       }
> +success_exit:
> +       return 0;
>
> +fail_free_dma_buf:
> +       dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
> +                       info->data_buff, info->data_buff_phys);
>  fail_free_buf:
> +       for (cs = 0; cs < pdata->num_cs; cs++)
> +               free_cs_resource(info, cs);
> +fail_free_irq:
>        free_irq(irq, info);
> -       if (use_dma) {
> -               pxa_free_dma(info->data_dma_ch);
> -               dma_free_coherent(&pdev->dev, info->data_buff_size,
> -                       info->data_buff, info->data_buff_phys);
> -       } else
> -               kfree(info->data_buff);
>  fail_free_io:
>        iounmap(info->mmio_base);
>  fail_free_res:
> @@ -1091,18 +1161,20 @@ fail_free_res:
>  fail_put_clk:
>        clk_disable(info->clk);
>        clk_put(info->clk);
> -fail_free_mtd:
> -       kfree(mtd);
> -       return NULL;
> +fail_alloc:
> +       kfree(info);
> +       return ret;
>  }
>
>  static int pxa3xx_nand_remove(struct platform_device *pdev)
>  {
>        struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> -       struct mtd_info *mtd = info->mtd;
> +       struct pxa3xx_nand_platform_data *pdata;
> +       struct pxa3xx_nand_host *host;
>        struct resource *r;
> -       int irq;
> +       int irq, cs;
>
> +       pdata = pdev->dev.platform_data;
>        platform_set_drvdata(pdev, NULL);
>
>        irq = platform_get_irq(pdev, 0);
> @@ -1110,7 +1182,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
>                free_irq(irq, info);
>        if (use_dma) {
>                pxa_free_dma(info->data_dma_ch);
> -               dma_free_writecombine(&pdev->dev, info->data_buff_size,
> +               dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
>                                info->data_buff, info->data_buff_phys);
>        } else
>                kfree(info->data_buff);
> @@ -1122,9 +1194,12 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
>        clk_disable(info->clk);
>        clk_put(info->clk);
>
> -       if (mtd) {
> -               nand_release(mtd);
> -               kfree(mtd);
> +       for (cs = 0; cs < pdata->num_cs; cs++) {
> +               host = info->host[cs];
> +               if (!host)
> +                       continue;
> +               nand_release(host->mtd);
> +               free_cs_resource(info, cs);
>        }
>        return 0;
>  }
> @@ -1133,6 +1208,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
>  {
>        struct pxa3xx_nand_platform_data *pdata;
>        struct pxa3xx_nand_info *info;
> +       int cs, ret, probe_success = 0;
>
>        pdata = pdev->dev.platform_data;
>        if (!pdata) {
> @@ -1140,18 +1216,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
>                return -ENODEV;
>        }
>
> -       info = alloc_nand_resource(pdev);
> -       if (info == NULL)
> -               return -ENOMEM;
> +       if (pdata->keep_config && pdata->num_cs > 1) {
> +               dev_warn(&pdev->dev, "keep_config is prohibited with multiple"
> +                        " chip selects feature!\n");
> +               pdata->keep_config = 0;
> +       }
> +
> +       ret = alloc_nand_resource(pdev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "alloc nand resource failed\n");
> +               return ret;
> +       }
> +
> +       info = platform_get_drvdata(pdev);
> +       for (cs = 0; cs < pdata->num_cs; cs++) {
> +               ret = pxa3xx_nand_scan(info->host[cs]->mtd);
> +               if (ret) {
> +                       dev_err(&pdev->dev, "failed to scan nand\n");
> +                       continue;
> +               }
> +
> +               ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
> +                       pdata->parts[cs], pdata->nr_parts[cs]);
> +               if (!ret)
> +                       probe_success = 1;
> +       }
>
> -       if (pxa3xx_nand_scan(info->mtd)) {
> -               dev_err(&pdev->dev, "failed to scan nand\n");
> +       if (!probe_success) {
>                pxa3xx_nand_remove(pdev);
>                return -ENODEV;
>        }
>
> -       return mtd_device_parse_register(info->mtd, NULL, 0,
> -                       pdata->parts, pdata->nr_parts);
> +       return 0;
>  }
>
>  #ifdef CONFIG_PM
> @@ -1171,8 +1267,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>  {
>        struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>
> -       nand_writel(info, NDTR0CS0, info->ndtr0cs0);
> -       nand_writel(info, NDTR1CS0, info->ndtr1cs0);
> +       /*
> +        * Directly set the chip select to a invalid value,
> +        * then the driver would reset the timing according
> +        * to current chip select at the beginning of cmdfunc
> +        */
> +       info->cs = 0xff;
>        clk_enable(info->clk);
>
>        return 0;
> --
> 1.7.0.4
>
>

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-25 12:32           ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-06-25 12:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Jun 25, 2011 at 1:17 PM, Lei Wen <leiwen@marvell.com> wrote:
> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
>
> Update platform driver to support this feature.
>
> Another 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.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>

I tested this on a PXA303 platform with one chipselect only, at at
least I can say that this patch doesn't seem to cause any regression
here. But I couldn't test the new feature it adds.

So, FWIW:

Tested-by: Daniel Mack <zonque@gmail.com>


Thanks!
Daniel


> ---
> ?arch/arm/mach-mmp/aspenite.c ? ? ? ? ? ? ? ? | ? ?5 +-
> ?arch/arm/mach-pxa/cm-x300.c ? ? ? ? ? ? ? ? ?| ? ?5 +-
> ?arch/arm/mach-pxa/colibri-pxa3xx.c ? ? ? ? ? | ? ?5 +-
> ?arch/arm/mach-pxa/littleton.c ? ? ? ? ? ? ? ?| ? ?5 +-
> ?arch/arm/mach-pxa/mxm8x10.c ? ? ? ? ? ? ? ? ?| ? ?9 +-
> ?arch/arm/mach-pxa/raumfeld.c ? ? ? ? ? ? ? ? | ? ?5 +-
> ?arch/arm/mach-pxa/zylonite.c ? ? ? ? ? ? ? ? | ? ?5 +-
> ?arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | ? 19 +-
> ?drivers/mtd/nand/pxa3xx_nand.c ? ? ? ? ? ? ? | ?512 +++++++++++++++-----------
> ?9 files changed, 346 insertions(+), 224 deletions(-)
>
> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
> index 06b5fa8..c4996f3 100644
> --- a/arch/arm/mach-mmp/aspenite.c
> +++ b/arch/arm/mach-mmp/aspenite.c
> @@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
>
> ?static struct pxa3xx_nand_platform_data aspenite_nand_info = {
> ? ? ? ?.enable_arbiter = 1,
> - ? ? ? .parts ? ? ? ? ?= aspenite_nand_partitions,
> - ? ? ? .nr_parts ? ? ? = ARRAY_SIZE(aspenite_nand_partitions),
> + ? ? ? .num_cs = 1,
> + ? ? ? .parts[0] ? ? ? = aspenite_nand_partitions,
> + ? ? ? .nr_parts[0] ? ?= ARRAY_SIZE(aspenite_nand_partitions),
> ?};
>
> ?static struct i2c_board_info aspenite_i2c_info[] __initdata = {
> diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
> index b2248e7..969d252 100644
> --- a/arch/arm/mach-pxa/cm-x300.c
> +++ b/arch/arm/mach-pxa/cm-x300.c
> @@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
> ?static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
> ? ? ? ?.enable_arbiter = 1,
> ? ? ? ?.keep_config ? ?= 1,
> - ? ? ? .parts ? ? ? ? ?= cm_x300_nand_partitions,
> - ? ? ? .nr_parts ? ? ? = ARRAY_SIZE(cm_x300_nand_partitions),
> + ? ? ? .num_cs ? ? ? ? = 1,
> + ? ? ? .parts[0] ? ? ? = cm_x300_nand_partitions,
> + ? ? ? .nr_parts[0] ? ?= ARRAY_SIZE(cm_x300_nand_partitions),
> ?};
>
> ?static void __init cm_x300_init_nand(void)
> diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
> index 3f9be41..2b8ca0d 100644
> --- a/arch/arm/mach-pxa/colibri-pxa3xx.c
> +++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
> @@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
> ?static struct pxa3xx_nand_platform_data colibri_nand_info = {
> ? ? ? ?.enable_arbiter = 1,
> ? ? ? ?.keep_config ? ?= 1,
> - ? ? ? .parts ? ? ? ? ?= colibri_nand_partitions,
> - ? ? ? .nr_parts ? ? ? = ARRAY_SIZE(colibri_nand_partitions),
> + ? ? ? .num_cs ? ? ? ? = 1,
> + ? ? ? .parts[0] ? ? ? = colibri_nand_partitions,
> + ? ? ? .nr_parts[0] ? ?= ARRAY_SIZE(colibri_nand_partitions),
> ?};
>
> ?void __init colibri_pxa3xx_init_nand(void)
> diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
> index e5e326d..f6d1f9d 100644
> --- a/arch/arm/mach-pxa/littleton.c
> +++ b/arch/arm/mach-pxa/littleton.c
> @@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
>
> ?static struct pxa3xx_nand_platform_data littleton_nand_info = {
> ? ? ? ?.enable_arbiter = 1,
> - ? ? ? .parts ? ? ? ? ?= littleton_nand_partitions,
> - ? ? ? .nr_parts ? ? ? = ARRAY_SIZE(littleton_nand_partitions),
> + ? ? ? .num_cs ? ? ? ? = 1,
> + ? ? ? .parts[0] ? ? ? = littleton_nand_partitions,
> + ? ? ? .nr_parts[0] ? ?= ARRAY_SIZE(littleton_nand_partitions),
> ?};
>
> ?static void __init littleton_init_nand(void)
> diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
> index b5a8fd3..90928d6 100644
> --- a/arch/arm/mach-pxa/mxm8x10.c
> +++ b/arch/arm/mach-pxa/mxm8x10.c
> @@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
> ?};
>
> ?static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
> - ? ? ? .enable_arbiter = 1,
> - ? ? ? .keep_config = 1,
> - ? ? ? .parts = mxm_8x10_nand_partitions,
> - ? ? ? .nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
> + ? ? ? .enable_arbiter = 1,
> + ? ? ? .keep_config ? ?= 1,
> + ? ? ? .num_cs ? ? ? ? = 1,
> + ? ? ? .parts[0] ? ? ? = mxm_8x10_nand_partitions,
> + ? ? ? .nr_parts[0] ? ?= ARRAY_SIZE(mxm_8x10_nand_partitions)
> ?};
>
> ?static void __init mxm_8x10_nand_init(void)
> diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
> index d130f77..c7ec847 100644
> --- a/arch/arm/mach-pxa/raumfeld.c
> +++ b/arch/arm/mach-pxa/raumfeld.c
> @@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
> ?static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
> ? ? ? ?.enable_arbiter = 1,
> ? ? ? ?.keep_config ? ?= 1,
> - ? ? ? .parts ? ? ? ? ?= raumfeld_nand_partitions,
> - ? ? ? .nr_parts ? ? ? = ARRAY_SIZE(raumfeld_nand_partitions),
> + ? ? ? .num_cs ? ? ? ? = 1,
> + ? ? ? .parts[0] ? ? ? = raumfeld_nand_partitions,
> + ? ? ? .nr_parts[0] ? ?= ARRAY_SIZE(raumfeld_nand_partitions),
> ?};
>
> ?/**
> diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
> index 5821185..64fdac9 100644
> --- a/arch/arm/mach-pxa/zylonite.c
> +++ b/arch/arm/mach-pxa/zylonite.c
> @@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
>
> ?static struct pxa3xx_nand_platform_data zylonite_nand_info = {
> ? ? ? ?.enable_arbiter = 1,
> - ? ? ? .parts ? ? ? ? ?= zylonite_nand_partitions,
> - ? ? ? .nr_parts ? ? ? = ARRAY_SIZE(zylonite_nand_partitions),
> + ? ? ? .num_cs ? ? ? ? = 1,
> + ? ? ? .parts[0] ? ? ? = zylonite_nand_partitions,
> + ? ? ? .nr_parts[0] ? ?= ARRAY_SIZE(zylonite_nand_partitions),
> ?};
>
> ?static void __init zylonite_init_nand(void)
> diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> index 442301f..4e17309 100644
> --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> @@ -41,6 +41,19 @@ struct pxa3xx_nand_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
> @@ -52,8 +65,10 @@ struct pxa3xx_nand_platform_data {
> ? ? ? ?/* allow platform code to keep OBM/bootloader defined NFC config */
> ? ? ? ?int ? ? keep_config;
>
> - ? ? ? const struct mtd_partition ? ? ? ? ? ? ?*parts;
> - ? ? ? unsigned int ? ? ? ? ? ? ? ? ? ? ? ? ? ?nr_parts;
> + ? ? ? /* indicate how many chip selects will be used */
> + ? ? ? int ? ? num_cs;
> + ? ? ? 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;
> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index b7db1b2..0825b7d 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -110,6 +110,7 @@ enum {
>
> ?enum {
> ? ? ? ?STATE_IDLE = 0,
> + ? ? ? STATE_PREPARED,
> ? ? ? ?STATE_CMD_HANDLE,
> ? ? ? ?STATE_DMA_READING,
> ? ? ? ?STATE_DMA_WRITING,
> @@ -120,21 +121,40 @@ enum {
> ? ? ? ?STATE_READY,
> ?};
>
> -struct pxa3xx_nand_info {
> - ? ? ? struct nand_chip ? ? ? ?nand_chip;
> +struct pxa3xx_nand_host {
> + ? ? ? struct nand_chip ? ? ? ?chip;
> + ? ? ? struct pxa3xx_nand_cmdset *cmdset;
> + ? ? ? struct mtd_info ? ? ? ? *mtd;
> + ? ? ? void ? ? ? ? ? ? ? ? ? ?*info_data;
>
> + ? ? ? /* page size of attached chip */
> + ? ? ? unsigned int ? ? ? ? ? ?page_size;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? cs;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? use_ecc;
> +
> + ? ? ? /* calculated from pxa3xx_nand_flash data */
> + ? ? ? unsigned int ? ? ? ? ? ?col_addr_cycles;
> + ? ? ? unsigned int ? ? ? ? ? ?row_addr_cycles;
> + ? ? ? size_t ? ? ? ? ? ? ? ? ?read_id_bytes;
> +
> + ? ? ? /* cached register value */
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?reg_ndcr;
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr0cs0;
> + ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr1cs0;
> +};
> +
> +struct pxa3xx_nand_info {
> ? ? ? ?struct nand_hw_control ?controller;
> ? ? ? ?struct platform_device ? *pdev;
> - ? ? ? struct pxa3xx_nand_cmdset *cmdset;
>
> ? ? ? ?struct clk ? ? ? ? ? ? ?*clk;
> ? ? ? ?void __iomem ? ? ? ? ? ?*mmio_base;
> ? ? ? ?unsigned long ? ? ? ? ? mmio_phys;
> + ? ? ? struct completion ? ? ? cmd_complete;
>
> ? ? ? ?unsigned int ? ? ? ? ? ?buf_start;
> ? ? ? ?unsigned int ? ? ? ? ? ?buf_count;
>
> - ? ? ? struct mtd_info ? ? ? ? *mtd;
> ? ? ? ?/* DMA information */
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? drcmr_dat;
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? drcmr_cmd;
> @@ -142,44 +162,27 @@ struct pxa3xx_nand_info {
> ? ? ? ?unsigned char ? ? ? ? ? *data_buff;
> ? ? ? ?unsigned char ? ? ? ? ? *oob_buff;
> ? ? ? ?dma_addr_t ? ? ? ? ? ? ?data_buff_phys;
> - ? ? ? size_t ? ? ? ? ? ? ? ? ?data_buff_size;
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? data_dma_ch;
> ? ? ? ?struct pxa_dma_desc ? ? *data_desc;
> ? ? ? ?dma_addr_t ? ? ? ? ? ? ?data_desc_addr;
>
> - ? ? ? uint32_t ? ? ? ? ? ? ? ?reg_ndcr;
> -
> - ? ? ? /* saved column/page_addr during CMD_SEQIN */
> - ? ? ? int ? ? ? ? ? ? ? ? ? ? seqin_column;
> - ? ? ? int ? ? ? ? ? ? ? ? ? ? seqin_page_addr;
> -
> + ? ? ? struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
> ? ? ? ?/* relate to the command */
> ? ? ? ?unsigned int ? ? ? ? ? ?state;
>
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? cs;
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? use_ecc; ? ? ? ?/* use HW ECC ? */
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? use_dma; ? ? ? ?/* use DMA ? */
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? is_ready;
>
> - ? ? ? unsigned int ? ? ? ? ? ?page_size; ? ? ?/* page size of attached chip */
> ? ? ? ?unsigned int ? ? ? ? ? ?data_size; ? ? ?/* data size in FIFO */
> + ? ? ? unsigned int ? ? ? ? ? ?oob_size;
> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? retcode;
> - ? ? ? struct completion ? ? ? cmd_complete;
>
> ? ? ? ?/* generated NDCBx register values */
> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb0;
> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb1;
> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb2;
> -
> - ? ? ? /* timing calcuted from setting */
> - ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr0cs0;
> - ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr1cs0;
> -
> - ? ? ? /* calculated from pxa3xx_nand_flash data */
> - ? ? ? size_t ? ? ? ? ?oob_size;
> - ? ? ? size_t ? ? ? ? ?read_id_bytes;
> -
> - ? ? ? unsigned int ? ?col_addr_cycles;
> - ? ? ? unsigned int ? ?row_addr_cycles;
> ?};
>
> ?static int use_dma = 1;
> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
> ?/* Define a default flash type setting serve as flash detecting only */
> ?#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>
> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>
> ?#define NDTR0_tCH(c) ? (min((c), 7) << 19)
> ?#define NDTR0_tCS(c) ? (min((c), 7) << 16)
> @@ -241,9 +244,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
> ?/* convert nano-seconds to nand flash controller clock cycles */
> ?#define ns2cycle(ns, clk) ? ? ?(int)((ns) * (clk / 1000000) / 1000)
>
> -static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
> +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const struct pxa3xx_nand_timing *t)
> ?{
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
> ? ? ? ?unsigned long nand_clk = clk_get_rate(info->clk);
> ? ? ? ?uint32_t ndtr0, ndtr1;
>
> @@ -258,23 +262,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
> ? ? ? ? ? ? ? ?NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
> ? ? ? ? ? ? ? ?NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
>
> - ? ? ? info->ndtr0cs0 = ndtr0;
> - ? ? ? info->ndtr1cs0 = ndtr1;
> + ? ? ? host->ndtr0cs0 = ndtr0;
> + ? ? ? host->ndtr1cs0 = ndtr1;
> ? ? ? ?nand_writel(info, NDTR0CS0, ndtr0);
> ? ? ? ?nand_writel(info, NDTR1CS0, ndtr1);
> ?}
>
> ?static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
> ?{
> - ? ? ? int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
> + ? ? ? struct pxa3xx_nand_host *host = info->host[info->cs];
> + ? ? ? int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
>
> - ? ? ? info->data_size = info->page_size;
> + ? ? ? info->data_size = host->page_size;
> ? ? ? ?if (!oob_enable) {
> ? ? ? ? ? ? ? ?info->oob_size = 0;
> ? ? ? ? ? ? ? ?return;
> ? ? ? ?}
>
> - ? ? ? switch (info->page_size) {
> + ? ? ? switch (host->page_size) {
> ? ? ? ?case 2048:
> ? ? ? ? ? ? ? ?info->oob_size = (info->use_ecc) ? 40 : 64;
> ? ? ? ? ? ? ? ?break;
> @@ -292,9 +297,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
> ?*/
> ?static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
> ?{
> + ? ? ? struct pxa3xx_nand_host *host = info->host[info->cs];
> ? ? ? ?uint32_t ndcr;
>
> - ? ? ? ndcr = info->reg_ndcr;
> + ? ? ? ndcr = host->reg_ndcr;
> ? ? ? ?ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
> ? ? ? ?ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
> ? ? ? ?ndcr |= NDCR_ND_RUN;
> @@ -359,8 +365,8 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?DIV_ROUND_UP(info->oob_size, 4));
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?default:
> - ? ? ? ? ? ? ? printk(KERN_ERR "%s: invalid state %d\n", __func__,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->state);
> + ? ? ? ? ? ? ? dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
> + ? ? ? ? ? ? ? ? ? ? ? info->state);
> ? ? ? ? ? ? ? ?BUG();
> ? ? ? ?}
> ?}
> @@ -385,7 +391,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
> ? ? ? ? ? ? ? ?desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?default:
> - ? ? ? ? ? ? ? printk(KERN_ERR "%s: invalid state %d\n", __func__,
> + ? ? ? ? ? ? ? dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?info->state);
> ? ? ? ? ? ? ? ?BUG();
> ? ? ? ?}
> @@ -416,9 +422,17 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = devid;
> ? ? ? ?unsigned int status, is_completed = 0;
> + ? ? ? unsigned int ready, cmd_done;
> +
> + ? ? ? if (info->cs == 0) {
> + ? ? ? ? ? ? ? ready ? ? ? ? ? = NDSR_FLASH_RDY;
> + ? ? ? ? ? ? ? cmd_done ? ? ? ?= NDSR_CS0_CMDD;
> + ? ? ? } else {
> + ? ? ? ? ? ? ? ready ? ? ? ? ? = NDSR_RDY;
> + ? ? ? ? ? ? ? cmd_done ? ? ? ?= NDSR_CS1_CMDD;
> + ? ? ? }
>
> ? ? ? ?status = nand_readl(info, NDSR);
> -
> ? ? ? ?if (status & NDSR_DBERR)
> ? ? ? ? ? ? ? ?info->retcode = ERR_DBERR;
> ? ? ? ?if (status & NDSR_SBERR)
> @@ -437,11 +451,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
> ? ? ? ? ? ? ? ? ? ? ? ?handle_data_pio(info);
> ? ? ? ? ? ? ? ?}
> ? ? ? ?}
> - ? ? ? if (status & NDSR_CS0_CMDD) {
> + ? ? ? if (status & cmd_done) {
> ? ? ? ? ? ? ? ?info->state = STATE_CMD_DONE;
> ? ? ? ? ? ? ? ?is_completed = 1;
> ? ? ? ?}
> - ? ? ? if (status & NDSR_FLASH_RDY) {
> + ? ? ? if (status & ready) {
> ? ? ? ? ? ? ? ?info->is_ready = 1;
> ? ? ? ? ? ? ? ?info->state = STATE_READY;
> ? ? ? ?}
> @@ -463,12 +477,6 @@ NORMAL_IRQ_EXIT:
> ? ? ? ?return IRQ_HANDLED;
> ?}
>
> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
> -{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> - ? ? ? return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
> -}
> -
> ?static inline int is_buf_blank(uint8_t *buf, size_t len)
> ?{
> ? ? ? ?for (; len > 0; len--)
> @@ -481,10 +489,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ? ? ? ? ?uint16_t column, int page_addr)
> ?{
> ? ? ? ?uint16_t cmd;
> - ? ? ? int addr_cycle, exec_cmd, ndcb0;
> - ? ? ? struct mtd_info *mtd = info->mtd;
> + ? ? ? int addr_cycle, exec_cmd;
> + ? ? ? struct pxa3xx_nand_host *host = info->host[info->cs];
> + ? ? ? struct mtd_info *mtd = host->mtd;
>
> - ? ? ? ndcb0 = 0;
> ? ? ? ?addr_cycle = 0;
> ? ? ? ?exec_cmd = 1;
>
> @@ -495,6 +503,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ?info->use_ecc ? ? ? ? ? = 0;
> ? ? ? ?info->is_ready ? ? ? ? ?= 0;
> ? ? ? ?info->retcode ? ? ? ? ? = ERR_NONE;
> + ? ? ? if (info->cs != 0)
> + ? ? ? ? ? ? ? info->ndcb0 = NDCB0_CSEL;
> + ? ? ? else
> + ? ? ? ? ? ? ? info->ndcb0 = 0;
>
> ? ? ? ?switch (command) {
> ? ? ? ?case NAND_CMD_READ0:
> @@ -512,20 +524,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?}
>
> - ? ? ? info->ndcb0 = ndcb0;
> - ? ? ? addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? + info->col_addr_cycles);
> + ? ? ? addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? + host->col_addr_cycles);
>
> ? ? ? ?switch (command) {
> ? ? ? ?case NAND_CMD_READOOB:
> ? ? ? ?case NAND_CMD_READ0:
> - ? ? ? ? ? ? ? cmd = info->cmdset->read1;
> + ? ? ? ? ? ? ? cmd = host->cmdset->read1;
> ? ? ? ? ? ? ? ?if (command == NAND_CMD_READOOB)
> ? ? ? ? ? ? ? ? ? ? ? ?info->buf_start = mtd->writesize + column;
> ? ? ? ? ? ? ? ?else
> ? ? ? ? ? ? ? ? ? ? ? ?info->buf_start = column;
>
> - ? ? ? ? ? ? ? if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
> + ? ? ? ? ? ? ? if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
> ? ? ? ? ? ? ? ? ? ? ? ?info->ndcb0 |= NDCB0_CMD_TYPE(0)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| addr_cycle
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| (cmd & NDCB0_CMD1_MASK);
> @@ -537,7 +548,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>
> ? ? ? ?case NAND_CMD_SEQIN:
> ? ? ? ? ? ? ? ?/* small page addr setting */
> - ? ? ? ? ? ? ? if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
> + ? ? ? ? ? ? ? if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
> ? ? ? ? ? ? ? ? ? ? ? ?info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| (column & 0xFF);
>
> @@ -564,7 +575,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ? ? ? ? ? ? ? ? ?break;
> ? ? ? ? ? ? ? ?}
>
> - ? ? ? ? ? ? ? cmd = info->cmdset->program;
> + ? ? ? ? ? ? ? cmd = host->cmdset->program;
> ? ? ? ? ? ? ? ?info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_AUTO_RS
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ST_ROW_EN
> @@ -574,8 +585,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ? ? ? ? ?break;
>
> ? ? ? ?case NAND_CMD_READID:
> - ? ? ? ? ? ? ? cmd = info->cmdset->read_id;
> - ? ? ? ? ? ? ? info->buf_count = info->read_id_bytes;
> + ? ? ? ? ? ? ? cmd = host->cmdset->read_id;
> + ? ? ? ? ? ? ? info->buf_count = host->read_id_bytes;
> ? ? ? ? ? ? ? ?info->ndcb0 |= NDCB0_CMD_TYPE(3)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(1)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
> @@ -583,7 +594,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ? ? ? ? ?info->data_size = 8;
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?case NAND_CMD_STATUS:
> - ? ? ? ? ? ? ? cmd = info->cmdset->read_status;
> + ? ? ? ? ? ? ? cmd = host->cmdset->read_status;
> ? ? ? ? ? ? ? ?info->buf_count = 1;
> ? ? ? ? ? ? ? ?info->ndcb0 |= NDCB0_CMD_TYPE(4)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(1)
> @@ -593,7 +604,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ? ? ? ? ? ? ? ?break;
>
> ? ? ? ?case NAND_CMD_ERASE1:
> - ? ? ? ? ? ? ? cmd = info->cmdset->erase;
> + ? ? ? ? ? ? ? cmd = host->cmdset->erase;
> ? ? ? ? ? ? ? ?info->ndcb0 |= NDCB0_CMD_TYPE(2)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_AUTO_RS
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(3)
> @@ -604,7 +615,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?case NAND_CMD_RESET:
> - ? ? ? ? ? ? ? cmd = info->cmdset->reset;
> + ? ? ? ? ? ? ? cmd = host->cmdset->reset;
> ? ? ? ? ? ? ? ?info->ndcb0 |= NDCB0_CMD_TYPE(5)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>
> @@ -616,7 +627,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>
> ? ? ? ?default:
> ? ? ? ? ? ? ? ?exec_cmd = 0;
> - ? ? ? ? ? ? ? printk(KERN_ERR "pxa3xx-nand: non-supported"
> + ? ? ? ? ? ? ? dev_err(&info->pdev->dev, "pxa3xx-nand: non-supported"
> ? ? ? ? ? ? ? ? ? ? ? ?" command %x\n", command);
> ? ? ? ? ? ? ? ?break;
> ? ? ? ?}
> @@ -627,7 +638,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> ?static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int column, int page_addr)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand_host *host = mtd->priv;
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
> ? ? ? ?int ret, exec_cmd;
>
> ? ? ? ?/*
> @@ -635,9 +647,21 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
> ? ? ? ? * "byte" address into a "word" address appropriate
> ? ? ? ? * for indexing a word-oriented device
> ? ? ? ? */
> - ? ? ? if (info->reg_ndcr & NDCR_DWIDTH_M)
> + ? ? ? if (host->reg_ndcr & NDCR_DWIDTH_M)
> ? ? ? ? ? ? ? ?column /= 2;
>
> + ? ? ? /*
> + ? ? ? ?* There may be different NAND chip hooked to
> + ? ? ? ?* different chip select, so check whether
> + ? ? ? ?* chip select has been changed, if yes, reset the timing
> + ? ? ? ?*/
> + ? ? ? if (info->cs != host->cs) {
> + ? ? ? ? ? ? ? info->cs = host->cs;
> + ? ? ? ? ? ? ? nand_writel(info, NDTR0CS0, host->ndtr0cs0);
> + ? ? ? ? ? ? ? nand_writel(info, NDTR1CS0, host->ndtr1cs0);
> + ? ? ? }
> +
> + ? ? ? info->state = STATE_PREPARED;
> ? ? ? ?exec_cmd = prepare_command_pool(info, command, column, page_addr);
> ? ? ? ?if (exec_cmd) {
> ? ? ? ? ? ? ? ?init_completion(&info->cmd_complete);
> @@ -646,12 +670,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
> ? ? ? ? ? ? ? ?ret = wait_for_completion_timeout(&info->cmd_complete,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CHIP_DELAY_TIMEOUT);
> ? ? ? ? ? ? ? ?if (!ret) {
> - ? ? ? ? ? ? ? ? ? ? ? printk(KERN_ERR "Wait time out!!!\n");
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(&info->pdev->dev, "Wait time out!!!\n");
> ? ? ? ? ? ? ? ? ? ? ? ?/* Stop State Machine for next command cycle */
> ? ? ? ? ? ? ? ? ? ? ? ?pxa3xx_nand_stop(info);
> ? ? ? ? ? ? ? ?}
> - ? ? ? ? ? ? ? info->state = STATE_IDLE;
> ? ? ? ?}
> + ? ? ? info->state = STATE_IDLE;
> ?}
>
> ?static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
> @@ -664,7 +688,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
> ?static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
> ? ? ? ? ? ? ? ?struct nand_chip *chip, uint8_t *buf, int page)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand_host *host = mtd->priv;
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
>
> ? ? ? ?chip->read_buf(mtd, buf, mtd->writesize);
> ? ? ? ?chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
> @@ -695,7 +720,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>
> ?static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand_host *host = mtd->priv;
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
> ? ? ? ?char retval = 0xFF;
>
> ? ? ? ?if (info->buf_start < info->buf_count)
> @@ -707,7 +733,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>
> ?static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand_host *host = mtd->priv;
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
> ? ? ? ?u16 retval = 0xFFFF;
>
> ? ? ? ?if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
> @@ -719,7 +746,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>
> ?static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand_host *host = mtd->priv;
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
> ? ? ? ?int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>
> ? ? ? ?memcpy(buf, info->data_buff + info->buf_start, real_len);
> @@ -729,7 +757,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> ?static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
> ? ? ? ? ? ? ? ?const uint8_t *buf, int len)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand_host *host = mtd->priv;
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
> ? ? ? ?int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>
> ? ? ? ?memcpy(info->data_buff + info->buf_start, buf, real_len);
> @@ -749,7 +778,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>
> ?static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand_host *host = mtd->priv;
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
>
> ? ? ? ?/* pxa3xx_nand_send_command has waited for command complete */
> ? ? ? ?if (this->state == FL_WRITING || this->state == FL_ERASING) {
> @@ -772,54 +802,69 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
> ?{
> ? ? ? ?struct platform_device *pdev = info->pdev;
> ? ? ? ?struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
> + ? ? ? struct pxa3xx_nand_host *host = info->host[info->cs];
> ? ? ? ?uint32_t ndcr = 0x0; /* enable all interrupts */
>
> - ? ? ? if (f->page_size != 2048 && f->page_size != 512)
> + ? ? ? if (f->page_size != 2048 && f->page_size != 512) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
> ? ? ? ? ? ? ? ?return -EINVAL;
> + ? ? ? }
>
> - ? ? ? if (f->flash_width != 16 && f->flash_width != 8)
> + ? ? ? if (f->flash_width != 16 && f->flash_width != 8) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
> ? ? ? ? ? ? ? ?return -EINVAL;
> + ? ? ? }
>
> ? ? ? ?/* calculate flash information */
> - ? ? ? info->cmdset = &default_cmdset;
> - ? ? ? info->page_size = f->page_size;
> - ? ? ? info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
> + ? ? ? host->cmdset = &default_cmdset;
> + ? ? ? host->page_size = f->page_size;
> + ? ? ? host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
>
> ? ? ? ?/* calculate addressing information */
> - ? ? ? info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
> + ? ? ? host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
>
> ? ? ? ?if (f->num_blocks * f->page_per_block > 65536)
> - ? ? ? ? ? ? ? info->row_addr_cycles = 3;
> + ? ? ? ? ? ? ? host->row_addr_cycles = 3;
> ? ? ? ?else
> - ? ? ? ? ? ? ? info->row_addr_cycles = 2;
> + ? ? ? ? ? ? ? host->row_addr_cycles = 2;
>
> ? ? ? ?ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
> - ? ? ? ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
> + ? ? ? 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(info->read_id_bytes);
> + ? ? ? ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
> ? ? ? ?ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>
> - ? ? ? info->reg_ndcr = ndcr;
> + ? ? ? host->reg_ndcr = ndcr;
>
> - ? ? ? pxa3xx_nand_set_timing(info, f->timing);
> + ? ? ? pxa3xx_nand_set_timing(host, f->timing);
> ? ? ? ?return 0;
> ?}
>
> ?static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
> ?{
> + ? ? ? /*
> + ? ? ? ?* 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
> + ? ? ? ?*/
> + ? ? ? struct pxa3xx_nand_host *host = info->host[0];
> ? ? ? ?uint32_t ndcr = nand_readl(info, NDCR);
> - ? ? ? info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
> - ? ? ? /* set info fields needed to read id */
> - ? ? ? info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
> - ? ? ? info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> - ? ? ? info->cmdset = &default_cmdset;
>
> - ? ? ? info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> - ? ? ? info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
> + ? ? ? if (ndcr & NDCR_PAGE_SZ) {
> + ? ? ? ? ? ? ? host->page_size = 2048;
> + ? ? ? ? ? ? ? host->read_id_bytes = 4;
> + ? ? ? } else {
> + ? ? ? ? ? ? ? host->page_size = 512;
> + ? ? ? ? ? ? ? host->read_id_bytes = 2;
> + ? ? ? }
> + ? ? ? host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> + ? ? ? host->cmdset = &default_cmdset;
> +
> + ? ? ? host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> + ? ? ? host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
>
> ? ? ? ?return 0;
> ?}
> @@ -829,59 +874,41 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
> ?* data buffer and the DMA descriptor
> ?*/
> ?#define MAX_BUFF_SIZE ?PAGE_SIZE
> -
> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
> ?{
> - ? ? ? struct platform_device *pdev = info->pdev;
> - ? ? ? int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
> -
> - ? ? ? if (use_dma == 0) {
> - ? ? ? ? ? ? ? info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> - ? ? ? ? ? ? ? if (info->data_buff == NULL)
> - ? ? ? ? ? ? ? ? ? ? ? return -ENOMEM;
> - ? ? ? ? ? ? ? return 0;
> - ? ? ? }
> -
> - ? ? ? info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &info->data_buff_phys, GFP_KERNEL);
> - ? ? ? if (info->data_buff == NULL) {
> - ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> - ? ? ? ? ? ? ? return -ENOMEM;
> - ? ? ? }
> -
> - ? ? ? info->data_buff_size = MAX_BUFF_SIZE;
> - ? ? ? info->data_desc = (void *)info->data_buff + data_desc_offset;
> - ? ? ? info->data_desc_addr = info->data_buff_phys + data_desc_offset;
> + ? ? ? struct pxa3xx_nand_host *host;
> + ? ? ? struct mtd_info *mtd;
>
> - ? ? ? info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_data_dma_irq, info);
> - ? ? ? if (info->data_dma_ch < 0) {
> - ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to request data dma\n");
> - ? ? ? ? ? ? ? dma_free_coherent(&pdev->dev, info->data_buff_size,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->data_buff, info->data_buff_phys);
> - ? ? ? ? ? ? ? return info->data_dma_ch;
> - ? ? ? }
> + ? ? ? if (!info->host[cs])
> + ? ? ? ? ? ? ? return;
>
> - ? ? ? return 0;
> + ? ? ? host = info->host[cs];
> + ? ? ? mtd = host->mtd;
> + ? ? ? kfree(mtd);
> + ? ? ? info->host[cs] = NULL;
> ?}
>
> ?static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
> ?{
> - ? ? ? struct mtd_info *mtd = info->mtd;
> - ? ? ? struct nand_chip *chip = mtd->priv;
> + ? ? ? struct mtd_info *mtd = info->host[info->cs]->mtd;
> + ? ? ? int ret;
>
> ? ? ? ?/* use the common timing to make a try */
> - ? ? ? pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
> - ? ? ? chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
> + ? ? ? ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? return ret;
> +
> + ? ? ? pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
> ? ? ? ?if (info->is_ready)
> - ? ? ? ? ? ? ? return 1;
> - ? ? ? else
> ? ? ? ? ? ? ? ?return 0;
> +
> + ? ? ? return -ENODEV;
> ?}
>
> ?static int pxa3xx_nand_scan(struct mtd_info *mtd)
> ?{
> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
> + ? ? ? struct pxa3xx_nand_host *host = mtd->priv;
> + ? ? ? struct pxa3xx_nand_info *info = host->info_data;
> ? ? ? ?struct platform_device *pdev = info->pdev;
> ? ? ? ?struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
> ? ? ? ?struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
> @@ -891,26 +918,26 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
> ? ? ? ?uint64_t chipsize;
> ? ? ? ?int i, ret, num;
>
> + ? ? ? info->cs = host->cs;
> ? ? ? ?if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
> ? ? ? ? ? ? ? ?goto KEEP_CONFIG;
>
> ? ? ? ?ret = pxa3xx_nand_sensing(info);
> - ? ? ? if (!ret) {
> - ? ? ? ? ? ? ? kfree(mtd);
> - ? ? ? ? ? ? ? info->mtd = NULL;
> - ? ? ? ? ? ? ? printk(KERN_INFO "There is no nand chip on cs 0!\n");
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? free_cs_resource(info, info->cs);
> + ? ? ? ? ? ? ? dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
>
> - ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? ? ? ? ? return ret;
> ? ? ? ?}
>
> ? ? ? ?chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
> ? ? ? ?id = *((uint16_t *)(info->data_buff));
> ? ? ? ?if (id != 0)
> - ? ? ? ? ? ? ? printk(KERN_INFO "Detect a flash id %x\n", id);
> + ? ? ? ? ? ? ? dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
> ? ? ? ?else {
> - ? ? ? ? ? ? ? kfree(mtd);
> - ? ? ? ? ? ? ? info->mtd = NULL;
> - ? ? ? ? ? ? ? printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
> + ? ? ? ? ? ? ? free_cs_resource(info, info->cs);
> + ? ? ? ? ? ? ? dev_warn(&info->pdev->dev, "Read out ID 0, "
> + ? ? ? ? ? ? ? ? ? ? ? ?"potential timing set wrong!!\n");
>
> ? ? ? ? ? ? ? ?return -EINVAL;
> ? ? ? ?}
> @@ -928,14 +955,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
> ? ? ? ?}
>
> ? ? ? ?if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
> - ? ? ? ? ? ? ? kfree(mtd);
> - ? ? ? ? ? ? ? info->mtd = NULL;
> - ? ? ? ? ? ? ? printk(KERN_ERR "ERROR!! flash not defined!!!\n");
> + ? ? ? ? ? ? ? free_cs_resource(info, info->cs);
> + ? ? ? ? ? ? ? dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
>
> ? ? ? ? ? ? ? ?return -EINVAL;
> ? ? ? ?}
>
> - ? ? ? pxa3xx_nand_config_flash(info, f);
> + ? ? ? ret = pxa3xx_nand_config_flash(info, f);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> ? ? ? ?pxa3xx_flash_ids[0].name = f->name;
> ? ? ? ?pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
> ? ? ? ?pxa3xx_flash_ids[0].pagesize = f->page_size;
> @@ -950,66 +980,51 @@ KEEP_CONFIG:
> ? ? ? ?if (nand_scan_ident(mtd, 1, def))
> ? ? ? ? ? ? ? ?return -ENODEV;
> ? ? ? ?/* calculate addressing information */
> - ? ? ? info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
> + ? ? ? if (mtd->writesize >= 2048)
> + ? ? ? ? ? ? ? host->col_addr_cycles = 2;
> + ? ? ? else
> + ? ? ? ? ? ? ? host->col_addr_cycles = 1;
> ? ? ? ?info->oob_buff = info->data_buff + mtd->writesize;
> ? ? ? ?if ((mtd->size >> chip->page_shift) > 65536)
> - ? ? ? ? ? ? ? info->row_addr_cycles = 3;
> + ? ? ? ? ? ? ? host->row_addr_cycles = 3;
> ? ? ? ?else
> - ? ? ? ? ? ? ? info->row_addr_cycles = 2;
> - ? ? ? mtd->name = mtd_names[0];
> + ? ? ? ? ? ? ? host->row_addr_cycles = 2;
> + ? ? ? mtd->name = mtd_names[info->cs];
> ? ? ? ?chip->ecc.mode = NAND_ECC_HW;
> - ? ? ? chip->ecc.size = info->page_size;
> + ? ? ? chip->ecc.size = host->page_size;
>
> - ? ? ? chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
> + ? ? ? chip->options = 0;
> + ? ? ? if (host->reg_ndcr & NDCR_DWIDTH_M)
> + ? ? ? ? ? ? ? chip->options = NAND_BUSWIDTH_16;
> ? ? ? ?chip->options |= NAND_NO_AUTOINCR;
> ? ? ? ?chip->options |= NAND_NO_READRDY;
>
> ? ? ? ?return nand_scan_tail(mtd);
> ?}
>
> -static
> -struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
> +static int alloc_nand_resource(struct platform_device *pdev)
> ?{
> + ? ? ? struct pxa3xx_nand_platform_data *pdata;
> ? ? ? ?struct pxa3xx_nand_info *info;
> + ? ? ? struct pxa3xx_nand_host *host;
> ? ? ? ?struct nand_chip *chip;
> ? ? ? ?struct mtd_info *mtd;
> ? ? ? ?struct resource *r;
> - ? ? ? int ret, irq;
> + ? ? ? int ret, irq, cs;
> + ? ? ? int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>
> - ? ? ? mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
> - ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
> - ? ? ? if (!mtd) {
> + ? ? ? pdata = pdev->dev.platform_data;
> + ? ? ? info = kzalloc(sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
> + ? ? ? if (!info) {
> ? ? ? ? ? ? ? ?dev_err(&pdev->dev, "failed to allocate memory\n");
> - ? ? ? ? ? ? ? return NULL;
> + ? ? ? ? ? ? ? return -ENOMEM;
> ? ? ? ?}
> -
> - ? ? ? info = (struct pxa3xx_nand_info *)(&mtd[1]);
> - ? ? ? chip = (struct nand_chip *)(&mtd[1]);
> ? ? ? ?info->pdev = pdev;
> - ? ? ? info->mtd = mtd;
> - ? ? ? mtd->priv = info;
> - ? ? ? mtd->owner = THIS_MODULE;
> -
> - ? ? ? chip->ecc.read_page ? ? = pxa3xx_nand_read_page_hwecc;
> - ? ? ? chip->ecc.write_page ? ?= pxa3xx_nand_write_page_hwecc;
> - ? ? ? chip->controller ? ? ? ?= &info->controller;
> - ? ? ? chip->waitfunc ? ? ? ? ?= pxa3xx_nand_waitfunc;
> - ? ? ? chip->select_chip ? ? ? = pxa3xx_nand_select_chip;
> - ? ? ? chip->dev_ready ? ? ? ? = pxa3xx_nand_dev_ready;
> - ? ? ? chip->cmdfunc ? ? ? ? ? = pxa3xx_nand_cmdfunc;
> - ? ? ? chip->read_word ? ? ? ? = pxa3xx_nand_read_word;
> - ? ? ? chip->read_byte ? ? ? ? = pxa3xx_nand_read_byte;
> - ? ? ? chip->read_buf ? ? ? ? ?= pxa3xx_nand_read_buf;
> - ? ? ? chip->write_buf ? ? ? ? = pxa3xx_nand_write_buf;
> - ? ? ? chip->verify_buf ? ? ? ?= pxa3xx_nand_verify_buf;
> -
> - ? ? ? spin_lock_init(&chip->controller->lock);
> - ? ? ? init_waitqueue_head(&chip->controller->wq);
> ? ? ? ?info->clk = clk_get(&pdev->dev, NULL);
> ? ? ? ?if (IS_ERR(info->clk)) {
> ? ? ? ? ? ? ? ?dev_err(&pdev->dev, "failed to get nand clock\n");
> ? ? ? ? ? ? ? ?ret = PTR_ERR(info->clk);
> - ? ? ? ? ? ? ? goto fail_free_mtd;
> + ? ? ? ? ? ? ? goto fail_alloc;
> ? ? ? ?}
> ? ? ? ?clk_enable(info->clk);
>
> @@ -1058,10 +1073,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
> ? ? ? ?}
> ? ? ? ?info->mmio_phys = r->start;
>
> - ? ? ? ret = pxa3xx_nand_init_buff(info);
> - ? ? ? if (ret)
> - ? ? ? ? ? ? ? goto fail_free_io;
> -
> ? ? ? ?/* initialize all interrupts to be disabled */
> ? ? ? ?disable_int(info, NDSR_MASK);
>
> @@ -1069,21 +1080,80 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
> ? ? ? ? ? ? ? ? ? ? ? ? ?pdev->name, info);
> ? ? ? ?if (ret < 0) {
> ? ? ? ? ? ? ? ?dev_err(&pdev->dev, "failed to request IRQ\n");
> - ? ? ? ? ? ? ? goto fail_free_buf;
> + ? ? ? ? ? ? ? ret = ENXIO;
> + ? ? ? ? ? ? ? goto fail_free_io;
> ? ? ? ?}
>
> ? ? ? ?platform_set_drvdata(pdev, info);
>
> - ? ? ? return info;
> + ? ? ? for (cs = 0; cs < pdata->num_cs; cs++) {
> + ? ? ? ? ? ? ? mtd = kzalloc(sizeof(struct mtd_info)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? + sizeof(struct pxa3xx_nand_host),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
> + ? ? ? ? ? ? ? if (!mtd) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate memory\n");
> + ? ? ? ? ? ? ? ? ? ? ? ret = -ENOMEM;
> + ? ? ? ? ? ? ? ? ? ? ? goto fail_free_irq;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? host = (struct pxa3xx_nand_host *)(&mtd[1]);
> + ? ? ? ? ? ? ? host->info_data = info;
> + ? ? ? ? ? ? ? host->cs = cs;
> + ? ? ? ? ? ? ? host->mtd = mtd;
> + ? ? ? ? ? ? ? mtd->priv = host;
> + ? ? ? ? ? ? ? mtd->owner = THIS_MODULE;
> + ? ? ? ? ? ? ? info->host[cs] = host;
> +
> + ? ? ? ? ? ? ? chip = (struct nand_chip *)(&mtd[1]);
> + ? ? ? ? ? ? ? chip->ecc.read_page ? ? = pxa3xx_nand_read_page_hwecc;
> + ? ? ? ? ? ? ? chip->ecc.write_page ? ?= pxa3xx_nand_write_page_hwecc;
> + ? ? ? ? ? ? ? chip->waitfunc ? ? ? ? ?= pxa3xx_nand_waitfunc;
> + ? ? ? ? ? ? ? chip->select_chip ? ? ? = pxa3xx_nand_select_chip;
> + ? ? ? ? ? ? ? chip->cmdfunc ? ? ? ? ? = pxa3xx_nand_cmdfunc;
> + ? ? ? ? ? ? ? chip->read_word ? ? ? ? = pxa3xx_nand_read_word;
> + ? ? ? ? ? ? ? chip->read_byte ? ? ? ? = pxa3xx_nand_read_byte;
> + ? ? ? ? ? ? ? chip->read_buf ? ? ? ? ?= pxa3xx_nand_read_buf;
> + ? ? ? ? ? ? ? chip->write_buf ? ? ? ? = pxa3xx_nand_write_buf;
> + ? ? ? ? ? ? ? chip->verify_buf ? ? ? ?= pxa3xx_nand_verify_buf;
> + ? ? ? }
> +
> + ? ? ? if (use_dma == 0) {
> + ? ? ? ? ? ? ? info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> + ? ? ? ? ? ? ? if (info->data_buff == NULL) {
> + ? ? ? ? ? ? ? ? ? ? ? ret = -ENOMEM;
> + ? ? ? ? ? ? ? ? ? ? ? goto fail_free_buf;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? goto success_exit;
> + ? ? ? }
> +
> + ? ? ? info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> + ? ? ? ? ? ? ? ? ? ? ? &info->data_buff_phys, GFP_KERNEL);
> + ? ? ? if (info->data_buff == NULL) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> + ? ? ? ? ? ? ? ret = -ENOMEM;
> + ? ? ? ? ? ? ? goto fail_free_buf;
> + ? ? ? }
> +
> + ? ? ? info->data_desc = (void *)info->data_buff + data_desc_offset;
> + ? ? ? info->data_desc_addr = info->data_buff_phys + data_desc_offset;
> + ? ? ? info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_data_dma_irq, info);
> + ? ? ? if (info->data_dma_ch < 0) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to request data dma\n");
> + ? ? ? ? ? ? ? ret = -ENXIO;
> + ? ? ? ? ? ? ? goto fail_free_dma_buf;
> + ? ? ? }
> +success_exit:
> + ? ? ? return 0;
>
> +fail_free_dma_buf:
> + ? ? ? dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
> + ? ? ? ? ? ? ? ? ? ? ? info->data_buff, info->data_buff_phys);
> ?fail_free_buf:
> + ? ? ? for (cs = 0; cs < pdata->num_cs; cs++)
> + ? ? ? ? ? ? ? free_cs_resource(info, cs);
> +fail_free_irq:
> ? ? ? ?free_irq(irq, info);
> - ? ? ? if (use_dma) {
> - ? ? ? ? ? ? ? pxa_free_dma(info->data_dma_ch);
> - ? ? ? ? ? ? ? dma_free_coherent(&pdev->dev, info->data_buff_size,
> - ? ? ? ? ? ? ? ? ? ? ? info->data_buff, info->data_buff_phys);
> - ? ? ? } else
> - ? ? ? ? ? ? ? kfree(info->data_buff);
> ?fail_free_io:
> ? ? ? ?iounmap(info->mmio_base);
> ?fail_free_res:
> @@ -1091,18 +1161,20 @@ fail_free_res:
> ?fail_put_clk:
> ? ? ? ?clk_disable(info->clk);
> ? ? ? ?clk_put(info->clk);
> -fail_free_mtd:
> - ? ? ? kfree(mtd);
> - ? ? ? return NULL;
> +fail_alloc:
> + ? ? ? kfree(info);
> + ? ? ? return ret;
> ?}
>
> ?static int pxa3xx_nand_remove(struct platform_device *pdev)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> - ? ? ? struct mtd_info *mtd = info->mtd;
> + ? ? ? struct pxa3xx_nand_platform_data *pdata;
> + ? ? ? struct pxa3xx_nand_host *host;
> ? ? ? ?struct resource *r;
> - ? ? ? int irq;
> + ? ? ? int irq, cs;
>
> + ? ? ? pdata = pdev->dev.platform_data;
> ? ? ? ?platform_set_drvdata(pdev, NULL);
>
> ? ? ? ?irq = platform_get_irq(pdev, 0);
> @@ -1110,7 +1182,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
> ? ? ? ? ? ? ? ?free_irq(irq, info);
> ? ? ? ?if (use_dma) {
> ? ? ? ? ? ? ? ?pxa_free_dma(info->data_dma_ch);
> - ? ? ? ? ? ? ? dma_free_writecombine(&pdev->dev, info->data_buff_size,
> + ? ? ? ? ? ? ? dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?info->data_buff, info->data_buff_phys);
> ? ? ? ?} else
> ? ? ? ? ? ? ? ?kfree(info->data_buff);
> @@ -1122,9 +1194,12 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
> ? ? ? ?clk_disable(info->clk);
> ? ? ? ?clk_put(info->clk);
>
> - ? ? ? if (mtd) {
> - ? ? ? ? ? ? ? nand_release(mtd);
> - ? ? ? ? ? ? ? kfree(mtd);
> + ? ? ? for (cs = 0; cs < pdata->num_cs; cs++) {
> + ? ? ? ? ? ? ? host = info->host[cs];
> + ? ? ? ? ? ? ? if (!host)
> + ? ? ? ? ? ? ? ? ? ? ? continue;
> + ? ? ? ? ? ? ? nand_release(host->mtd);
> + ? ? ? ? ? ? ? free_cs_resource(info, cs);
> ? ? ? ?}
> ? ? ? ?return 0;
> ?}
> @@ -1133,6 +1208,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
> ?{
> ? ? ? ?struct pxa3xx_nand_platform_data *pdata;
> ? ? ? ?struct pxa3xx_nand_info *info;
> + ? ? ? int cs, ret, probe_success = 0;
>
> ? ? ? ?pdata = pdev->dev.platform_data;
> ? ? ? ?if (!pdata) {
> @@ -1140,18 +1216,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
> ? ? ? ? ? ? ? ?return -ENODEV;
> ? ? ? ?}
>
> - ? ? ? info = alloc_nand_resource(pdev);
> - ? ? ? if (info == NULL)
> - ? ? ? ? ? ? ? return -ENOMEM;
> + ? ? ? if (pdata->keep_config && pdata->num_cs > 1) {
> + ? ? ? ? ? ? ? dev_warn(&pdev->dev, "keep_config is prohibited with multiple"
> + ? ? ? ? ? ? ? ? ? ? ? ?" chip selects feature!\n");
> + ? ? ? ? ? ? ? pdata->keep_config = 0;
> + ? ? ? }
> +
> + ? ? ? ret = alloc_nand_resource(pdev);
> + ? ? ? if (ret) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "alloc nand resource failed\n");
> + ? ? ? ? ? ? ? return ret;
> + ? ? ? }
> +
> + ? ? ? info = platform_get_drvdata(pdev);
> + ? ? ? for (cs = 0; cs < pdata->num_cs; cs++) {
> + ? ? ? ? ? ? ? ret = pxa3xx_nand_scan(info->host[cs]->mtd);
> + ? ? ? ? ? ? ? if (ret) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to scan nand\n");
> + ? ? ? ? ? ? ? ? ? ? ? continue;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
> + ? ? ? ? ? ? ? ? ? ? ? pdata->parts[cs], pdata->nr_parts[cs]);
> + ? ? ? ? ? ? ? if (!ret)
> + ? ? ? ? ? ? ? ? ? ? ? probe_success = 1;
> + ? ? ? }
>
> - ? ? ? if (pxa3xx_nand_scan(info->mtd)) {
> - ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to scan nand\n");
> + ? ? ? if (!probe_success) {
> ? ? ? ? ? ? ? ?pxa3xx_nand_remove(pdev);
> ? ? ? ? ? ? ? ?return -ENODEV;
> ? ? ? ?}
>
> - ? ? ? return mtd_device_parse_register(info->mtd, NULL, 0,
> - ? ? ? ? ? ? ? ? ? ? ? pdata->parts, pdata->nr_parts);
> + ? ? ? return 0;
> ?}
>
> ?#ifdef CONFIG_PM
> @@ -1171,8 +1267,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
> ?{
> ? ? ? ?struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>
> - ? ? ? nand_writel(info, NDTR0CS0, info->ndtr0cs0);
> - ? ? ? nand_writel(info, NDTR1CS0, info->ndtr1cs0);
> + ? ? ? /*
> + ? ? ? ?* Directly set the chip select to a invalid value,
> + ? ? ? ?* then the driver would reset the timing according
> + ? ? ? ?* to current chip select at the beginning of cmdfunc
> + ? ? ? ?*/
> + ? ? ? info->cs = 0xff;
> ? ? ? ?clk_enable(info->clk);
>
> ? ? ? ?return 0;
> --
> 1.7.0.4
>
>

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-25 12:32           ` Daniel Mack
@ 2011-06-25 12:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-25 12:51 UTC (permalink / raw)
  To: Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, Igor Grinberg, linux-arm-kernel

Hi Daniel,

On Sat, Jun 25, 2011 at 8:32 PM, Daniel Mack <zonque@gmail.com> wrote:
> On Sat, Jun 25, 2011 at 1:17 PM, Lei Wen <leiwen@marvell.com> wrote:
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>
> I tested this on a PXA303 platform with one chipselect only, at at
> least I can say that this patch doesn't seem to cause any regression
> here. But I couldn't test the new feature it adds.
>
> So, FWIW:
>
> Tested-by: Daniel Mack <zonque@gmail.com>

Thanks for testing!

Best regards,
Lei

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-25 12:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-25 12:51 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Daniel,

On Sat, Jun 25, 2011 at 8:32 PM, Daniel Mack <zonque@gmail.com> wrote:
> On Sat, Jun 25, 2011 at 1:17 PM, Lei Wen <leiwen@marvell.com> wrote:
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>
> I tested this on a PXA303 platform with one chipselect only, at at
> least I can say that this patch doesn't seem to cause any regression
> here. But I couldn't test the new feature it adds.
>
> So, FWIW:
>
> Tested-by: Daniel Mack <zonque@gmail.com>

Thanks for testing!

Best regards,
Lei

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-25 11:17         ` Lei Wen
@ 2011-06-27 13:22           ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-06-27 13:22 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Haojian Zhuang,
	Daniel Mack, linux-mtd, linux-arm-kernel

On 06/25/11 14:17, Lei Wen wrote:

> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
>
> Update platform driver to support this feature.
>
> Another 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.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
>  9 files changed, 346 insertions(+), 224 deletions(-)

~100 lines less - nice!

[...]

> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index b7db1b2..0825b7d 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c

[...]

> @@ -616,7 +627,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  
>  	default:
>  		exec_cmd = 0;
> -		printk(KERN_ERR "pxa3xx-nand: non-supported"
> +		dev_err(&info->pdev->dev, "pxa3xx-nand: non-supported"

If you use dev_err(), why do you still need pxa3xx-nand in the message?


-- 
Regards,
Igor.

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-27 13:22           ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-06-27 13:22 UTC (permalink / raw)
  To: linux-arm-kernel

On 06/25/11 14:17, Lei Wen wrote:

> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
>
> Update platform driver to support this feature.
>
> Another 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.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
>  9 files changed, 346 insertions(+), 224 deletions(-)

~100 lines less - nice!

[...]

> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index b7db1b2..0825b7d 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c

[...]

> @@ -616,7 +627,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  
>  	default:
>  		exec_cmd = 0;
> -		printk(KERN_ERR "pxa3xx-nand: non-supported"
> +		dev_err(&info->pdev->dev, "pxa3xx-nand: non-supported"

If you use dev_err(), why do you still need pxa3xx-nand in the message?


-- 
Regards,
Igor.

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-25 11:17         ` Lei Wen
@ 2011-06-28  7:32           ` Artem Bityutskiy
  -1 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-06-28  7:32 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Haojian Zhuang, Daniel Mack,
	linux-mtd, Igor Grinberg, linux-arm-kernel

On Sat, 2011-06-25 at 04:17 -0700, Lei Wen wrote:
> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
> 
> Update platform driver to support this feature.
> 
> Another 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.
> 
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
>  9 files changed, 346 insertions(+), 224 deletions(-)

In general huge patches like this are not encouraged and it is better to
split them. At least the printk changes can be in a separate patch. Then
re-naming host to info probably could be another patch. Could you please
rather do a series of smaller patches? Also, if you use dev_info and the
like functions, you probably do not need the pxa3xx_nand prefix anymore.

-- 
Best Regards,
Artem Bityutskiy

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-28  7:32           ` Artem Bityutskiy
  0 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-06-28  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 2011-06-25 at 04:17 -0700, Lei Wen wrote:
> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
> 
> Update platform driver to support this feature.
> 
> Another 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.
> 
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
>  9 files changed, 346 insertions(+), 224 deletions(-)

In general huge patches like this are not encouraged and it is better to
split them. At least the printk changes can be in a separate patch. Then
re-naming host to info probably could be another patch. Could you please
rather do a series of smaller patches? Also, if you use dev_info and the
like functions, you probably do not need the pxa3xx_nand prefix anymore.

-- 
Best Regards,
Artem Bityutskiy

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

* Re: [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-28 15:12             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-28 15:12 UTC (permalink / raw)
  To: dedekind1, Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Lei Wen, Haojian Zhuang, Daniel Mack,
	linux-mtd, linux-arm-kernel

On Tue, Jun 28, 2011 at 3:32 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> On Sat, 2011-06-25 at 04:17 -0700, Lei Wen wrote:
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>>  arch/arm/mach-pxa/littleton.c                |    5 +-
>>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>>  drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
>>  9 files changed, 346 insertions(+), 224 deletions(-)
>
> In general huge patches like this are not encouraged and it is better to
> split them. At least the printk changes can be in a separate patch. Then
> re-naming host to info probably could be another patch. Could you please
> rather do a series of smaller patches? Also, if you use dev_info and the
> like functions, you probably do not need the pxa3xx_nand prefix anymore.
>

Hi Artem & Igor,

Thanks for your suggestion, I would seperate this patch and push back later.

Best regards,
Lei

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-28 15:12             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-28 15:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jun 28, 2011 at 3:32 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> On Sat, 2011-06-25 at 04:17 -0700, Lei Wen wrote:
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>> ?arch/arm/mach-mmp/aspenite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/cm-x300.c ? ? ? ? ? ? ? ? ?| ? ?5 +-
>> ?arch/arm/mach-pxa/colibri-pxa3xx.c ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/littleton.c ? ? ? ? ? ? ? ?| ? ?5 +-
>> ?arch/arm/mach-pxa/mxm8x10.c ? ? ? ? ? ? ? ? ?| ? ?9 +-
>> ?arch/arm/mach-pxa/raumfeld.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/zylonite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | ? 19 +-
>> ?drivers/mtd/nand/pxa3xx_nand.c ? ? ? ? ? ? ? | ?512 +++++++++++++++-----------
>> ?9 files changed, 346 insertions(+), 224 deletions(-)
>
> In general huge patches like this are not encouraged and it is better to
> split them. At least the printk changes can be in a separate patch. Then
> re-naming host to info probably could be another patch. Could you please
> rather do a series of smaller patches? Also, if you use dev_info and the
> like functions, you probably do not need the pxa3xx_nand prefix anymore.
>

Hi Artem & Igor,

Thanks for your suggestion, I would seperate this patch and push back later.

Best regards,
Lei

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

* [PATCH V3 0/9] pxa3xx_nand: add two chip select support
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

Lei Wen (9):
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: enable multiple chip select support
  ARM: aspenite: fix nand platform data
  ARM: cm-x300: fix nand platform data
  ARM: colibri-pxa3xx: fix nand platform data
  ARM: littleton: fix nand platform data
  ARM: mxm8x10: fix nand platform data
  ARM: raumfeld: fix nand platform data
  ARM: zylonite: fix nand platform data

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
 9 files changed, 346 insertions(+), 224 deletions(-)

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

* [PATCH V3 0/9] pxa3xx_nand: add two chip select support
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

Lei Wen (9):
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: enable multiple chip select support
  ARM: aspenite: fix nand platform data
  ARM: cm-x300: fix nand platform data
  ARM: colibri-pxa3xx: fix nand platform data
  ARM: littleton: fix nand platform data
  ARM: mxm8x10: fix nand platform data
  ARM: raumfeld: fix nand platform data
  ARM: zylonite: fix nand platform data

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
 9 files changed, 346 insertions(+), 224 deletions(-)

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

* [PATCH 1/9] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   19 ++++++++++---------
 1 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..44fb210 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -898,7 +898,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +906,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +931,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH 1/9] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   19 ++++++++++---------
 1 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..44fb210 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -898,7 +898,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +906,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +931,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH 2/9] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  493 +++++++++++++++----------
 2 files changed, 313 insertions(+), 199 deletions(-)

diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..4e17309 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,10 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 44fb210..3ffa6e0 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,40 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			cs;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
 
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,44 +162,27 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
+	struct pxa3xx_nand_host	*host[NUM_CHIP_SELECT];
 	/* relate to the command */
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
 
-	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -241,9 +244,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +262,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +297,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -416,9 +422,17 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
-
 	if (status & NDSR_DBERR)
 		info->retcode = ERR_DBERR;
 	if (status & NDSR_SBERR)
@@ -437,11 +451,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -463,12 +477,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +489,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -495,6 +503,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -512,20 +524,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +548,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +575,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +585,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +594,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +604,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +615,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +638,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +647,21 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +674,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +688,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +720,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +733,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +746,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +757,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +778,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,54 +802,69 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -829,59 +874,41 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
  * data buffer and the DMA descriptor
  */
 #define MAX_BUFF_SIZE	PAGE_SIZE
-
-static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
+static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
 {
-	struct platform_device *pdev = info->pdev;
-	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
-
-	if (use_dma == 0) {
-		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
-		if (info->data_buff == NULL)
-			return -ENOMEM;
-		return 0;
-	}
-
-	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
-				&info->data_buff_phys, GFP_KERNEL);
-	if (info->data_buff == NULL) {
-		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
-		return -ENOMEM;
-	}
-
-	info->data_buff_size = MAX_BUFF_SIZE;
-	info->data_desc = (void *)info->data_buff + data_desc_offset;
-	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
-	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
-				pxa3xx_nand_data_dma_irq, info);
-	if (info->data_dma_ch < 0) {
-		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-				info->data_buff, info->data_buff_phys);
-		return info->data_dma_ch;
-	}
+	if (!info->host[cs])
+		return;
 
-	return 0;
+	host = info->host[cs];
+	mtd = host->mtd;
+	kfree(mtd);
+	info->host[cs] = NULL;
 }
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host[info->cs]->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -891,16 +918,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	uint64_t chipsize;
 	int i, ret, num;
 
+	info->cs = host->cs;
 	if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
+		free_cs_resource(info, info->cs);
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -908,8 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
+		free_cs_resource(info, info->cs);
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -929,14 +955,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
+		free_cs_resource(info, info->cs);
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -951,66 +980,51 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
+		host->row_addr_cycles = 2;
+	mtd->name = mtd_names[info->cs];
 	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
+	chip->ecc.size = host->page_size;
 
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
+	chip->options = 0;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options = NAND_BUSWIDTH_16;
 	chip->options |= NAND_NO_AUTOINCR;
 	chip->options |= NAND_NO_READRDY;
 
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
+	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
-			GFP_KERNEL);
-	if (!mtd) {
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
-
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
-
-	spin_lock_init(&chip->controller->lock);
-	init_waitqueue_head(&chip->controller->wq);
 	info->clk = clk_get(&pdev->dev, NULL);
 	if (IS_ERR(info->clk)) {
 		dev_err(&pdev->dev, "failed to get nand clock\n");
 		ret = PTR_ERR(info->clk);
-		goto fail_free_mtd;
+		goto fail_alloc;
 	}
 	clk_enable(info->clk);
 
@@ -1059,10 +1073,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	}
 	info->mmio_phys = r->start;
 
-	ret = pxa3xx_nand_init_buff(info);
-	if (ret)
-		goto fail_free_io;
-
 	/* initialize all interrupts to be disabled */
 	disable_int(info, NDSR_MASK);
 
@@ -1070,21 +1080,80 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 			  pdev->name, info);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to request IRQ\n");
-		goto fail_free_buf;
+		ret = ENXIO;
+		goto fail_free_io;
 	}
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = kzalloc(sizeof(struct mtd_info)
+				+ sizeof(struct pxa3xx_nand_host),
+				GFP_KERNEL);
+		if (!mtd) {
+			dev_err(&pdev->dev, "failed to allocate memory\n");
+			ret = -ENOMEM;
+			goto fail_free_irq;
+		}
+
+		host = (struct pxa3xx_nand_host *)(&mtd[1]);
+		host->info_data = info;
+		host->cs = cs;
+		host->mtd = mtd;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+		info->host[cs] = host;
+
+		chip = (struct nand_chip *)(&mtd[1]);
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
+
+	if (use_dma == 0) {
+		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
+		if (info->data_buff == NULL) {
+			ret = -ENOMEM;
+			goto fail_free_buf;
+		}
+		goto success_exit;
+	}
+
+	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
+			&info->data_buff_phys, GFP_KERNEL);
+	if (info->data_buff == NULL) {
+		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
+		ret = -ENOMEM;
+		goto fail_free_buf;
+	}
+
+	info->data_desc = (void *)info->data_buff + data_desc_offset;
+	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
+				pxa3xx_nand_data_dma_irq, info);
+	if (info->data_dma_ch < 0) {
+		dev_err(&pdev->dev, "failed to request data dma\n");
+		ret = -ENXIO;
+		goto fail_free_dma_buf;
+	}
+success_exit:
+	return 0;
 
+fail_free_dma_buf:
+	dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
+			info->data_buff, info->data_buff_phys);
 fail_free_buf:
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		free_cs_resource(info, cs);
+fail_free_irq:
 	free_irq(irq, info);
-	if (use_dma) {
-		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-			info->data_buff, info->data_buff_phys);
-	} else
-		kfree(info->data_buff);
 fail_free_io:
 	iounmap(info->mmio_base);
 fail_free_res:
@@ -1092,18 +1161,20 @@ fail_free_res:
 fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
-fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+fail_alloc:
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand_platform_data *pdata;
+	struct pxa3xx_nand_host *host;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1111,7 +1182,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1123,9 +1194,12 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		host = info->host[cs];
+		if (!host)
+			continue;
+		nand_release(host->mtd);
+		free_cs_resource(info, cs);
 	}
 	return 0;
 }
@@ -1134,6 +1208,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int cs, ret, probe_success = 0;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1141,18 +1216,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	if (pdata->keep_config && pdata->num_cs > 1) {
+		dev_warn(&pdev->dev, "keep_config is prohibited with multiple"
+			 " chip selects feature!\n");
+		pdata->keep_config = 0;
+	}
+
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
+
+	info = platform_get_drvdata(pdev);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to scan nand\n");
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+			pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1172,8 +1267,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 2/9] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  493 +++++++++++++++----------
 2 files changed, 313 insertions(+), 199 deletions(-)

diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..4e17309 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,10 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 44fb210..3ffa6e0 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,40 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			cs;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
 
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,44 +162,27 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
+	struct pxa3xx_nand_host	*host[NUM_CHIP_SELECT];
 	/* relate to the command */
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
 
-	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -241,9 +244,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +262,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +297,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -416,9 +422,17 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
-
 	if (status & NDSR_DBERR)
 		info->retcode = ERR_DBERR;
 	if (status & NDSR_SBERR)
@@ -437,11 +451,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -463,12 +477,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +489,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -495,6 +503,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -512,20 +524,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +548,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +575,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +585,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +594,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +604,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +615,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +638,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +647,21 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +674,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +688,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +720,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +733,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +746,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +757,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +778,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,54 +802,69 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -829,59 +874,41 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
  * data buffer and the DMA descriptor
  */
 #define MAX_BUFF_SIZE	PAGE_SIZE
-
-static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
+static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
 {
-	struct platform_device *pdev = info->pdev;
-	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
-
-	if (use_dma == 0) {
-		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
-		if (info->data_buff == NULL)
-			return -ENOMEM;
-		return 0;
-	}
-
-	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
-				&info->data_buff_phys, GFP_KERNEL);
-	if (info->data_buff == NULL) {
-		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
-		return -ENOMEM;
-	}
-
-	info->data_buff_size = MAX_BUFF_SIZE;
-	info->data_desc = (void *)info->data_buff + data_desc_offset;
-	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
-	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
-				pxa3xx_nand_data_dma_irq, info);
-	if (info->data_dma_ch < 0) {
-		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-				info->data_buff, info->data_buff_phys);
-		return info->data_dma_ch;
-	}
+	if (!info->host[cs])
+		return;
 
-	return 0;
+	host = info->host[cs];
+	mtd = host->mtd;
+	kfree(mtd);
+	info->host[cs] = NULL;
 }
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host[info->cs]->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -891,16 +918,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	uint64_t chipsize;
 	int i, ret, num;
 
+	info->cs = host->cs;
 	if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
+		free_cs_resource(info, info->cs);
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -908,8 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
+		free_cs_resource(info, info->cs);
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -929,14 +955,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
+		free_cs_resource(info, info->cs);
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -951,66 +980,51 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
+		host->row_addr_cycles = 2;
+	mtd->name = mtd_names[info->cs];
 	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
+	chip->ecc.size = host->page_size;
 
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
+	chip->options = 0;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options = NAND_BUSWIDTH_16;
 	chip->options |= NAND_NO_AUTOINCR;
 	chip->options |= NAND_NO_READRDY;
 
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
+	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
-			GFP_KERNEL);
-	if (!mtd) {
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
-
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
-
-	spin_lock_init(&chip->controller->lock);
-	init_waitqueue_head(&chip->controller->wq);
 	info->clk = clk_get(&pdev->dev, NULL);
 	if (IS_ERR(info->clk)) {
 		dev_err(&pdev->dev, "failed to get nand clock\n");
 		ret = PTR_ERR(info->clk);
-		goto fail_free_mtd;
+		goto fail_alloc;
 	}
 	clk_enable(info->clk);
 
@@ -1059,10 +1073,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	}
 	info->mmio_phys = r->start;
 
-	ret = pxa3xx_nand_init_buff(info);
-	if (ret)
-		goto fail_free_io;
-
 	/* initialize all interrupts to be disabled */
 	disable_int(info, NDSR_MASK);
 
@@ -1070,21 +1080,80 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 			  pdev->name, info);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to request IRQ\n");
-		goto fail_free_buf;
+		ret = ENXIO;
+		goto fail_free_io;
 	}
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = kzalloc(sizeof(struct mtd_info)
+				+ sizeof(struct pxa3xx_nand_host),
+				GFP_KERNEL);
+		if (!mtd) {
+			dev_err(&pdev->dev, "failed to allocate memory\n");
+			ret = -ENOMEM;
+			goto fail_free_irq;
+		}
+
+		host = (struct pxa3xx_nand_host *)(&mtd[1]);
+		host->info_data = info;
+		host->cs = cs;
+		host->mtd = mtd;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+		info->host[cs] = host;
+
+		chip = (struct nand_chip *)(&mtd[1]);
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
+
+	if (use_dma == 0) {
+		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
+		if (info->data_buff == NULL) {
+			ret = -ENOMEM;
+			goto fail_free_buf;
+		}
+		goto success_exit;
+	}
+
+	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
+			&info->data_buff_phys, GFP_KERNEL);
+	if (info->data_buff == NULL) {
+		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
+		ret = -ENOMEM;
+		goto fail_free_buf;
+	}
+
+	info->data_desc = (void *)info->data_buff + data_desc_offset;
+	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
+				pxa3xx_nand_data_dma_irq, info);
+	if (info->data_dma_ch < 0) {
+		dev_err(&pdev->dev, "failed to request data dma\n");
+		ret = -ENXIO;
+		goto fail_free_dma_buf;
+	}
+success_exit:
+	return 0;
 
+fail_free_dma_buf:
+	dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
+			info->data_buff, info->data_buff_phys);
 fail_free_buf:
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		free_cs_resource(info, cs);
+fail_free_irq:
 	free_irq(irq, info);
-	if (use_dma) {
-		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-			info->data_buff, info->data_buff_phys);
-	} else
-		kfree(info->data_buff);
 fail_free_io:
 	iounmap(info->mmio_base);
 fail_free_res:
@@ -1092,18 +1161,20 @@ fail_free_res:
 fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
-fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+fail_alloc:
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand_platform_data *pdata;
+	struct pxa3xx_nand_host *host;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1111,7 +1182,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1123,9 +1194,12 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		host = info->host[cs];
+		if (!host)
+			continue;
+		nand_release(host->mtd);
+		free_cs_resource(info, cs);
 	}
 	return 0;
 }
@@ -1134,6 +1208,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int cs, ret, probe_success = 0;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1141,18 +1216,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	if (pdata->keep_config && pdata->num_cs > 1) {
+		dev_warn(&pdev->dev, "keep_config is prohibited with multiple"
+			 " chip selects feature!\n");
+		pdata->keep_config = 0;
+	}
+
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
+
+	info = platform_get_drvdata(pdev);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to scan nand\n");
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+			pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1172,8 +1267,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 3/9] ARM: aspenite: fix nand platform data
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
-- 
1.7.0.4

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

* [PATCH 3/9] ARM: aspenite: fix nand platform data
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
-- 
1.7.0.4

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

* [PATCH 4/9] ARM: cm-x300: fix nand platform data
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/cm-x300.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
-- 
1.7.0.4

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

* [PATCH 4/9] ARM: cm-x300: fix nand platform data
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/cm-x300.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
-- 
1.7.0.4

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

* [PATCH 5/9] ARM: colibri-pxa3xx: fix nand platform data
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/colibri-pxa3xx.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
-- 
1.7.0.4

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

* [PATCH 5/9] ARM: colibri-pxa3xx: fix nand platform data
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/colibri-pxa3xx.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
-- 
1.7.0.4

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

* [PATCH 6/9] ARM: littleton: fix nand platform data
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/littleton.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
-- 
1.7.0.4

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

* [PATCH 6/9] ARM: littleton: fix nand platform data
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/littleton.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
-- 
1.7.0.4

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

* [PATCH 7/9] ARM: mxm8x10: fix nand platform data
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/mxm8x10.c |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
-- 
1.7.0.4

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

* [PATCH 7/9] ARM: mxm8x10: fix nand platform data
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/mxm8x10.c |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
-- 
1.7.0.4

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

* [PATCH 8/9] ARM: raumfeld: fix nand platform data
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/raumfeld.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
-- 
1.7.0.4

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

* [PATCH 8/9] ARM: raumfeld: fix nand platform data
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/raumfeld.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
-- 
1.7.0.4

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

* [PATCH 9/9] ARM: zylonite: fix nand platform data
  2011-06-28  7:32           ` Artem Bityutskiy
@ 2011-06-29  3:51             ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/zylonite.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
-- 
1.7.0.4

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

* [PATCH 9/9] ARM: zylonite: fix nand platform data
@ 2011-06-29  3:51             ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  3:51 UTC (permalink / raw)
  To: linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-pxa/zylonite.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
-- 
1.7.0.4

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

* Re: [PATCH 2/9] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-29  3:51             ` Lei Wen
@ 2011-06-29  7:11               ` Artem Bityutskiy
  -1 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-06-29  7:11 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Haojian Zhuang, Daniel Mack,
	linux-mtd, Igor Grinberg, linux-arm-kernel

On Tue, 2011-06-28 at 20:51 -0700, Lei Wen wrote:
> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
> 
> Update platform driver to support this feature.
> 
> Another 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.
> 
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  493 +++++++++++++++----------
>  2 files changed, 313 insertions(+), 199 deletions(-)

If someone can review this patch - good let's see how many Reviewed-by
tags we get. But I think it is not very reviewable because of the size.

AFAICK, what you do here is

1. Introduce new structure struct pxa3xx_nand_host
2. Move several fields from struct pxa3xx_nand_info to struct
pxa3xx_nand_host

And only this alone adds a lot of noise to the patch.

And also you have some real functional changes.

So I'd suggest you to split this patch at least on 2 - the first one
would introduce struct pxa3xx_nand_host, moved some fields, and do all
the host<->info renamings. You can split this on several steps and move
only several fields at a time.

Then a separate patch would do some more functional changes. How does
this sound?

-- 
Best Regards,
Artem Bityutskiy

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

* [PATCH 2/9] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-29  7:11               ` Artem Bityutskiy
  0 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-06-29  7:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2011-06-28 at 20:51 -0700, Lei Wen wrote:
> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
> 
> Update platform driver to support this feature.
> 
> Another 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.
> 
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  493 +++++++++++++++----------
>  2 files changed, 313 insertions(+), 199 deletions(-)

If someone can review this patch - good let's see how many Reviewed-by
tags we get. But I think it is not very reviewable because of the size.

AFAICK, what you do here is

1. Introduce new structure struct pxa3xx_nand_host
2. Move several fields from struct pxa3xx_nand_info to struct
pxa3xx_nand_host

And only this alone adds a lot of noise to the patch.

And also you have some real functional changes.

So I'd suggest you to split this patch at least on 2 - the first one
would introduce struct pxa3xx_nand_host, moved some fields, and do all
the host<->info renamings. You can split this on several steps and move
only several fields at a time.

Then a separate patch would do some more functional changes. How does
this sound?

-- 
Best Regards,
Artem Bityutskiy

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

* Re: [PATCH 2/9] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-29  7:11               ` Artem Bityutskiy
@ 2011-06-29  7:16                 ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  7:16 UTC (permalink / raw)
  To: dedekind1
  Cc: Eric Miao, David Woodhouse, Lei Wen, Haojian Zhuang, Daniel Mack,
	linux-mtd, Igor Grinberg, linux-arm-kernel

Hi Artem,

On Wed, Jun 29, 2011 at 3:11 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> On Tue, 2011-06-28 at 20:51 -0700, Lei Wen wrote:
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>>  drivers/mtd/nand/pxa3xx_nand.c               |  493 +++++++++++++++----------
>>  2 files changed, 313 insertions(+), 199 deletions(-)
>
> If someone can review this patch - good let's see how many Reviewed-by
> tags we get. But I think it is not very reviewable because of the size.
>
> AFAICK, what you do here is
>
> 1. Introduce new structure struct pxa3xx_nand_host
> 2. Move several fields from struct pxa3xx_nand_info to struct
> pxa3xx_nand_host
>
> And only this alone adds a lot of noise to the patch.
>
> And also you have some real functional changes.
>
> So I'd suggest you to split this patch at least on 2 - the first one
> would introduce struct pxa3xx_nand_host, moved some fields, and do all
> the host<->info renamings. You can split this on several steps and move
> only several fields at a time.
>
> Then a separate patch would do some more functional changes. How does
> this sound?
>

I'm ok with this, patches to come.

Thanks,
Lei

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

* [PATCH 2/9] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-06-29  7:16                 ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-06-29  7:16 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Artem,

On Wed, Jun 29, 2011 at 3:11 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> On Tue, 2011-06-28 at 20:51 -0700, Lei Wen wrote:
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>> ?arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | ? 19 +-
>> ?drivers/mtd/nand/pxa3xx_nand.c ? ? ? ? ? ? ? | ?493 +++++++++++++++----------
>> ?2 files changed, 313 insertions(+), 199 deletions(-)
>
> If someone can review this patch - good let's see how many Reviewed-by
> tags we get. But I think it is not very reviewable because of the size.
>
> AFAICK, what you do here is
>
> 1. Introduce new structure struct pxa3xx_nand_host
> 2. Move several fields from struct pxa3xx_nand_info to struct
> pxa3xx_nand_host
>
> And only this alone adds a lot of noise to the patch.
>
> And also you have some real functional changes.
>
> So I'd suggest you to split this patch at least on 2 - the first one
> would introduce struct pxa3xx_nand_host, moved some fields, and do all
> the host<->info renamings. You can split this on several steps and move
> only several fields at a time.
>
> Then a separate patch would do some more functional changes. How does
> this sound?
>

I'm ok with this, patches to come.

Thanks,
Lei

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

* Re: [PATCH V3 0/9] pxa3xx_nand: add two chip select support
  2011-06-29  3:51             ` Lei Wen
@ 2011-06-29  9:00               ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-06-29  9:00 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Haojian Zhuang,
	Daniel Mack, linux-mtd, linux-arm-kernel

Hi Lei,

On 06/29/11 06:51, Lei Wen wrote:

> V2:
> Choose to at most keep original pxa3xx_nand_info structure to reduce
> patch size
>
> V3:
> Seperate original one patch into a patch set
>
> Lei Wen (9):
>   MTD: pxa3xx_nand: convert all printk into dev_*
>   MTD: pxa3xx_nand: enable multiple chip select support
>   ARM: aspenite: fix nand platform data
>   ARM: cm-x300: fix nand platform data
>   ARM: colibri-pxa3xx: fix nand platform data
>   ARM: littleton: fix nand platform data
>   ARM: mxm8x10: fix nand platform data
>   ARM: raumfeld: fix nand platform data
>   ARM: zylonite: fix nand platform data

While I agree with Artem, that reviewing patch sets are much more easier,
than a one single big patch, I think you should keep the
"fix nand platform data" changes all in one patch.
All of them do the same change, but for different boards.

>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
>  9 files changed, 346 insertions(+), 224 deletions(-)
>
>

-- 
Regards,
Igor.

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

* [PATCH V3 0/9] pxa3xx_nand: add two chip select support
@ 2011-06-29  9:00               ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-06-29  9:00 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lei,

On 06/29/11 06:51, Lei Wen wrote:

> V2:
> Choose to at most keep original pxa3xx_nand_info structure to reduce
> patch size
>
> V3:
> Seperate original one patch into a patch set
>
> Lei Wen (9):
>   MTD: pxa3xx_nand: convert all printk into dev_*
>   MTD: pxa3xx_nand: enable multiple chip select support
>   ARM: aspenite: fix nand platform data
>   ARM: cm-x300: fix nand platform data
>   ARM: colibri-pxa3xx: fix nand platform data
>   ARM: littleton: fix nand platform data
>   ARM: mxm8x10: fix nand platform data
>   ARM: raumfeld: fix nand platform data
>   ARM: zylonite: fix nand platform data

While I agree with Artem, that reviewing patch sets are much more easier,
than a one single big patch, I think you should keep the
"fix nand platform data" changes all in one patch.
All of them do the same change, but for different boards.

>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   19 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  512 +++++++++++++++-----------
>  9 files changed, 346 insertions(+), 224 deletions(-)
>
>

-- 
Regards,
Igor.

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

* Re: [PATCH V3 0/9] pxa3xx_nand: add two chip select support
  2011-06-29  9:00               ` Igor Grinberg
@ 2011-06-29 10:20                 ` Artem Bityutskiy
  -1 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-06-29 10:20 UTC (permalink / raw)
  To: Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Lei Wen, Haojian Zhuang, Daniel Mack,
	linux-mtd, linux-arm-kernel

On Wed, 2011-06-29 at 12:00 +0300, Igor Grinberg wrote:
> Hi Lei,
> 
> On 06/29/11 06:51, Lei Wen wrote:
> 
> > V2:
> > Choose to at most keep original pxa3xx_nand_info structure to reduce
> > patch size
> >
> > V3:
> > Seperate original one patch into a patch set
> >
> > Lei Wen (9):
> >   MTD: pxa3xx_nand: convert all printk into dev_*
> >   MTD: pxa3xx_nand: enable multiple chip select support
> >   ARM: aspenite: fix nand platform data
> >   ARM: cm-x300: fix nand platform data
> >   ARM: colibri-pxa3xx: fix nand platform data
> >   ARM: littleton: fix nand platform data
> >   ARM: mxm8x10: fix nand platform data
> >   ARM: raumfeld: fix nand platform data
> >   ARM: zylonite: fix nand platform data
> 
> While I agree with Artem, that reviewing patch sets are much more easier,
> than a one single big patch, I think you should keep the
> "fix nand platform data" changes all in one patch.
> All of them do the same change, but for different boards.

Yeah, I did not suggest to split a large patch to a number of
micro-patches plus another large patch :-) My suggestion is to have a
set of easily reviewable patches. If you do the same thing in many
platform data structures, it is reviewable and can be in one patch.

-- 
Best Regards,
Artem Bityutskiy

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

* [PATCH V3 0/9] pxa3xx_nand: add two chip select support
@ 2011-06-29 10:20                 ` Artem Bityutskiy
  0 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-06-29 10:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 2011-06-29 at 12:00 +0300, Igor Grinberg wrote:
> Hi Lei,
> 
> On 06/29/11 06:51, Lei Wen wrote:
> 
> > V2:
> > Choose to at most keep original pxa3xx_nand_info structure to reduce
> > patch size
> >
> > V3:
> > Seperate original one patch into a patch set
> >
> > Lei Wen (9):
> >   MTD: pxa3xx_nand: convert all printk into dev_*
> >   MTD: pxa3xx_nand: enable multiple chip select support
> >   ARM: aspenite: fix nand platform data
> >   ARM: cm-x300: fix nand platform data
> >   ARM: colibri-pxa3xx: fix nand platform data
> >   ARM: littleton: fix nand platform data
> >   ARM: mxm8x10: fix nand platform data
> >   ARM: raumfeld: fix nand platform data
> >   ARM: zylonite: fix nand platform data
> 
> While I agree with Artem, that reviewing patch sets are much more easier,
> than a one single big patch, I think you should keep the
> "fix nand platform data" changes all in one patch.
> All of them do the same change, but for different boards.

Yeah, I did not suggest to split a large patch to a number of
micro-patches plus another large patch :-) My suggestion is to have a
set of easily reviewable patches. If you do the same thing in many
platform data structures, it is reviewable and can be in one patch.

-- 
Best Regards,
Artem Bityutskiy

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

* [PATCH V4 0/4] pxa3xx_nand: add two chip select support
  2011-06-29  3:51             ` Lei Wen
@ 2011-07-04  9:25               ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

Lei Wen (4):
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support
  ARM: mmp/pxa: fix nand platform data

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 ++-
 drivers/mtd/nand/pxa3xx_nand.c               |  414 ++++++++++++++++----------
 9 files changed, 294 insertions(+), 179 deletions(-)

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

* [PATCH V4 0/4] pxa3xx_nand: add two chip select support
@ 2011-07-04  9:25               ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

Lei Wen (4):
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support
  ARM: mmp/pxa: fix nand platform data

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 ++-
 drivers/mtd/nand/pxa3xx_nand.c               |  414 ++++++++++++++++----------
 9 files changed, 294 insertions(+), 179 deletions(-)

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

* [PATCH 1/4] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-06-29  3:51             ` Lei Wen
@ 2011-07-04  9:25               ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..043c0bb 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH 1/4] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-07-04  9:25               ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..043c0bb 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH 2/4] MTD: pxa3xx_nand: sperate each chip individual info
  2011-06-29  3:51             ` Lei Wen
@ 2011-07-04  9:25               ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  281 ++++++++++++++++++++++------------------
 1 files changed, 154 insertions(+), 127 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 043c0bb..a16328a 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
 
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,51 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +866,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +873,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +883,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +915,11 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
+		host->mtd = NULL;
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +927,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
+		host->mtd = NULL;
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -933,14 +947,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
+		host->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -955,44 +972,52 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 	mtd->name = mtd_names[0];
 	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
+	chip->ecc.size = host->page_size;
 
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
+	chip->options = 0;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options = NAND_BUSWIDTH_16;
 	chip->options |= NAND_NO_AUTOINCR;
 	chip->options |= NAND_NO_READRDY;
 
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1025,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1103,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1121,18 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1140,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1152,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1161,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1169,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1176,8 +1203,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 2/4] MTD: pxa3xx_nand: sperate each chip individual info
@ 2011-07-04  9:25               ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  281 ++++++++++++++++++++++------------------
 1 files changed, 154 insertions(+), 127 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 043c0bb..a16328a 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
 
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,51 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +866,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +873,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +883,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +915,11 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
+		host->mtd = NULL;
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +927,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
+		host->mtd = NULL;
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -933,14 +947,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
+		host->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -955,44 +972,52 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 	mtd->name = mtd_names[0];
 	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
+	chip->ecc.size = host->page_size;
 
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
+	chip->options = 0;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options = NAND_BUSWIDTH_16;
 	chip->options |= NAND_NO_AUTOINCR;
 	chip->options |= NAND_NO_READRDY;
 
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1025,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1103,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1121,18 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1140,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1152,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1161,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1169,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1176,8 +1203,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-06-29  3:51             ` Lei Wen
@ 2011-07-04  9:25               ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  152 ++++++++++++++++++--------
 2 files changed, 124 insertions(+), 48 deletions(-)

diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index a16328a..66f1709 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -883,9 +914,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -917,7 +948,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
 		host->mtd = NULL;
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs %d!\n"
+				, info->cs);
 
 		return ret;
 	}
@@ -996,41 +1028,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1128,11 +1166,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
+
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1152,7 +1193,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1161,7 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1176,14 +1218,28 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-04  9:25               ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  152 ++++++++++++++++++--------
 2 files changed, 124 insertions(+), 48 deletions(-)

diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index a16328a..66f1709 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -883,9 +914,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -917,7 +948,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
 		host->mtd = NULL;
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs %d!\n"
+				, info->cs);
 
 		return ret;
 	}
@@ -996,41 +1028,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1128,11 +1166,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
+
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1152,7 +1193,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1161,7 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1176,14 +1218,28 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 4/4] ARM: mmp/pxa: fix nand platform data
  2011-06-29  3:51             ` Lei Wen
@ 2011-07-04  9:25               ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c       |    5 +++--
 arch/arm/mach-pxa/cm-x300.c        |    5 +++--
 arch/arm/mach-pxa/colibri-pxa3xx.c |    5 +++--
 arch/arm/mach-pxa/littleton.c      |    5 +++--
 arch/arm/mach-pxa/mxm8x10.c        |    9 +++++----
 arch/arm/mach-pxa/raumfeld.c       |    5 +++--
 arch/arm/mach-pxa/zylonite.c       |    5 +++--
 7 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
-- 
1.7.0.4

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

* [PATCH 4/4] ARM: mmp/pxa: fix nand platform data
@ 2011-07-04  9:25               ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

Since two chip select is supported, the original defined nand platform
data need to be fixed accordingly.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c       |    5 +++--
 arch/arm/mach-pxa/cm-x300.c        |    5 +++--
 arch/arm/mach-pxa/colibri-pxa3xx.c |    5 +++--
 arch/arm/mach-pxa/littleton.c      |    5 +++--
 arch/arm/mach-pxa/mxm8x10.c        |    9 +++++----
 arch/arm/mach-pxa/raumfeld.c       |    5 +++--
 arch/arm/mach-pxa/zylonite.c       |    5 +++--
 7 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
-- 
1.7.0.4

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

* Re: [PATCH V3 0/9] pxa3xx_nand: add two chip select support
  2011-06-29 10:20                 ` Artem Bityutskiy
@ 2011-07-04  9:27                   ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:27 UTC (permalink / raw)
  To: dedekind1, Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Lei Wen, Haojian Zhuang, Daniel Mack,
	linux-mtd, linux-arm-kernel

Hi Artem & Igor,

On Wed, Jun 29, 2011 at 6:20 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> On Wed, 2011-06-29 at 12:00 +0300, Igor Grinberg wrote:
>> Hi Lei,
>>
>> On 06/29/11 06:51, Lei Wen wrote:
>>
>> > V2:
>> > Choose to at most keep original pxa3xx_nand_info structure to reduce
>> > patch size
>> >
>> > V3:
>> > Seperate original one patch into a patch set
>> >
>> > Lei Wen (9):
>> >   MTD: pxa3xx_nand: convert all printk into dev_*
>> >   MTD: pxa3xx_nand: enable multiple chip select support
>> >   ARM: aspenite: fix nand platform data
>> >   ARM: cm-x300: fix nand platform data
>> >   ARM: colibri-pxa3xx: fix nand platform data
>> >   ARM: littleton: fix nand platform data
>> >   ARM: mxm8x10: fix nand platform data
>> >   ARM: raumfeld: fix nand platform data
>> >   ARM: zylonite: fix nand platform data
>>
>> While I agree with Artem, that reviewing patch sets are much more easier,
>> than a one single big patch, I think you should keep the
>> "fix nand platform data" changes all in one patch.
>> All of them do the same change, but for different boards.
>
> Yeah, I did not suggest to split a large patch to a number of
> micro-patches plus another large patch :-) My suggestion is to have a
> set of easily reviewable patches. If you do the same thing in many
> platform data structures, it is reviewable and can be in one patch.
>
V4 patch set has been pushed. Hope this time would be better. :)

Thanks,
Lei

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

* [PATCH V3 0/9] pxa3xx_nand: add two chip select support
@ 2011-07-04  9:27                   ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-04  9:27 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Artem & Igor,

On Wed, Jun 29, 2011 at 6:20 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> On Wed, 2011-06-29 at 12:00 +0300, Igor Grinberg wrote:
>> Hi Lei,
>>
>> On 06/29/11 06:51, Lei Wen wrote:
>>
>> > V2:
>> > Choose to at most keep original pxa3xx_nand_info structure to reduce
>> > patch size
>> >
>> > V3:
>> > Seperate original one patch into a patch set
>> >
>> > Lei Wen (9):
>> > ? MTD: pxa3xx_nand: convert all printk into dev_*
>> > ? MTD: pxa3xx_nand: enable multiple chip select support
>> > ? ARM: aspenite: fix nand platform data
>> > ? ARM: cm-x300: fix nand platform data
>> > ? ARM: colibri-pxa3xx: fix nand platform data
>> > ? ARM: littleton: fix nand platform data
>> > ? ARM: mxm8x10: fix nand platform data
>> > ? ARM: raumfeld: fix nand platform data
>> > ? ARM: zylonite: fix nand platform data
>>
>> While I agree with Artem, that reviewing patch sets are much more easier,
>> than a one single big patch, I think you should keep the
>> "fix nand platform data" changes all in one patch.
>> All of them do the same change, but for different boards.
>
> Yeah, I did not suggest to split a large patch to a number of
> micro-patches plus another large patch :-) My suggestion is to have a
> set of easily reviewable patches. If you do the same thing in many
> platform data structures, it is reviewable and can be in one patch.
>
V4 patch set has been pushed. Hope this time would be better. :)

Thanks,
Lei

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-04  9:25               ` Lei Wen
@ 2011-07-06  6:53                 ` Artem Bityutskiy
  -1 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-07-06  6:53 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Haojian Zhuang, Daniel Mack,
	linux-mtd, Igor Grinberg, linux-arm-kernel

The problem is that in this patch you change  struct pxa3xx_nand_platform_data,
and in the next patch you amend the platform data declarations, which means that
the kernel will not compile between patches 3 and 4...

On Mon, 2011-07-04 at 02:25 -0700, Lei Wen wrote:
> +               dev_info(&info->pdev->dev, "There is no nand chip on cs %d!\n"
> +                               , info->cs); 

Nitpick - keep the comma at the first line.

-- 
Best Regards,
Artem Bityutskiy

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-06  6:53                 ` Artem Bityutskiy
  0 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-07-06  6:53 UTC (permalink / raw)
  To: linux-arm-kernel

The problem is that in this patch you change  struct pxa3xx_nand_platform_data,
and in the next patch you amend the platform data declarations, which means that
the kernel will not compile between patches 3 and 4...

On Mon, 2011-07-04 at 02:25 -0700, Lei Wen wrote:
> +               dev_info(&info->pdev->dev, "There is no nand chip on cs %d!\n"
> +                               , info->cs); 

Nitpick - keep the comma at the first line.

-- 
Best Regards,
Artem Bityutskiy

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-06  6:53                 ` Artem Bityutskiy
@ 2011-07-06  6:54                   ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-06  6:54 UTC (permalink / raw)
  To: dedekind1
  Cc: Eric Miao, David Woodhouse, Lei Wen, Haojian Zhuang, Daniel Mack,
	linux-mtd, Igor Grinberg, linux-arm-kernel

On Wed, Jul 6, 2011 at 2:53 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> The problem is that in this patch you change  struct pxa3xx_nand_platform_data,
> and in the next patch you amend the platform data declarations, which means that
> the kernel will not compile between patches 3 and 4...

So... how about merge the patch 3 and 4 together?

>
> On Mon, 2011-07-04 at 02:25 -0700, Lei Wen wrote:
>> +               dev_info(&info->pdev->dev, "There is no nand chip on cs %d!\n"
>> +                               , info->cs);
>
> Nitpick - keep the comma at the first line.

Opps, sorry for this.

Thanks,
Lei

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-06  6:54                   ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-06  6:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jul 6, 2011 at 2:53 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> The problem is that in this patch you change ?struct pxa3xx_nand_platform_data,
> and in the next patch you amend the platform data declarations, which means that
> the kernel will not compile between patches 3 and 4...

So... how about merge the patch 3 and 4 together?

>
> On Mon, 2011-07-04 at 02:25 -0700, Lei Wen wrote:
>> + ? ? ? ? ? ? ? dev_info(&info->pdev->dev, "There is no nand chip on cs %d!\n"
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? , info->cs);
>
> Nitpick - keep the comma at the first line.

Opps, sorry for this.

Thanks,
Lei

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-06  6:54                   ` Lei Wen
@ 2011-07-06  7:07                     ` Artem Bityutskiy
  -1 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-07-06  7:07 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Lei Wen, Haojian Zhuang, Daniel Mack,
	linux-mtd, Igor Grinberg, linux-arm-kernel

On Wed, 2011-07-06 at 14:54 +0800, Lei Wen wrote:
> On Wed, Jul 6, 2011 at 2:53 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> > The problem is that in this patch you change  struct pxa3xx_nand_platform_data,
> > and in the next patch you amend the platform data declarations, which means that
> > the kernel will not compile between patches 3 and 4...
> 
> So... how about merge the patch 3 and 4 together?

IMO this should be OK.

-- 
Best Regards,
Artem Bityutskiy

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-06  7:07                     ` Artem Bityutskiy
  0 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-07-06  7:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 2011-07-06 at 14:54 +0800, Lei Wen wrote:
> On Wed, Jul 6, 2011 at 2:53 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> > The problem is that in this patch you change  struct pxa3xx_nand_platform_data,
> > and in the next patch you amend the platform data declarations, which means that
> > the kernel will not compile between patches 3 and 4...
> 
> So... how about merge the patch 3 and 4 together?

IMO this should be OK.

-- 
Best Regards,
Artem Bityutskiy

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

* Re: [PATCH 2/4] MTD: pxa3xx_nand: sperate each chip individual info
  2011-07-04  9:25               ` Lei Wen
@ 2011-07-06  7:29                 ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-06  7:29 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Haojian Zhuang,
	Daniel Mack, linux-mtd, linux-arm-kernel

Hi Lei,


You are going to resubmit the series to merge patches 3 and 4, so

a small nitpick below


On 07/04/11 12:25, Lei Wen wrote:

> For support two chip select, we seperate chip specific info in this
> patch.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  drivers/mtd/nand/pxa3xx_nand.c |  281 ++++++++++++++++++++++------------------
>  1 files changed, 154 insertions(+), 127 deletions(-)

[...]

>  
>  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>  {
> +	struct pxa3xx_nand_host *host = info->host;
>  	uint32_t ndcr = nand_readl(info, NDCR);
> -	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
> -	/* set info fields needed to read id */
> -	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
> -	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> -	info->cmdset = &default_cmdset;
>  
> -	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> -	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
> +	if (ndcr & NDCR_PAGE_SZ) {
> +		host->page_size = 2048;
> +		host->read_id_bytes = 4;
> +	} else {
> +		host->page_size = 512;
> +		host->read_id_bytes = 2;
> +	}

empty line would be nice here

> +	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> +	host->cmdset = &default_cmdset;
> +
> +	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> +	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
>  
>  	return 0;
>  }

[...]

>  
> @@ -933,14 +947,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  	}
>  
>  	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
> -		kfree(mtd);
> -		info->mtd = NULL;
> +		host->mtd = NULL;
>  		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
>  
>  		return -EINVAL;
>  	}
>  
> -	pxa3xx_nand_config_flash(info, f);
> +	ret = pxa3xx_nand_config_flash(info, f);
> +	if (ret) {
> +		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
> +		return ret;
> +	}

and here

>  	pxa3xx_flash_ids[0].name = f->name;
>  	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
>  	pxa3xx_flash_ids[0].pagesize = f->page_size;
> @@ -955,44 +972,52 @@ KEEP_CONFIG:
>  	if (nand_scan_ident(mtd, 1, def))
>  		return -ENODEV;
>  	/* calculate addressing information */
> -	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
> +	if (mtd->writesize >= 2048)
> +		host->col_addr_cycles = 2;
> +	else
> +		host->col_addr_cycles = 1;

and here

>  	info->oob_buff = info->data_buff + mtd->writesize;
>  	if ((mtd->size >> chip->page_shift) > 65536)
> -		info->row_addr_cycles = 3;
> +		host->row_addr_cycles = 3;
>  	else
> -		info->row_addr_cycles = 2;
> +		host->row_addr_cycles = 2;

also here

>  	mtd->name = mtd_names[0];
>  	chip->ecc.mode = NAND_ECC_HW;
> -	chip->ecc.size = info->page_size;
> +	chip->ecc.size = host->page_size;
>  
> -	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
> +	chip->options = 0;
> +	if (host->reg_ndcr & NDCR_DWIDTH_M)
> +		chip->options = NAND_BUSWIDTH_16;
>  	chip->options |= NAND_NO_AUTOINCR;
>  	chip->options |= NAND_NO_READRDY;

here, you can just reorder the bit operations, so you won't need to do options = 0.
And empty line after if, would be great.

>  
>  	return nand_scan_tail(mtd);
>  }
>  

[...]

>  static int pxa3xx_nand_remove(struct platform_device *pdev)
>  {
>  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> -	struct mtd_info *mtd = info->mtd;
>  	struct resource *r;
>  	int irq;
>  
> +	if (!info)
> +		return 0;

and here (empty line)

apart from that nitpicking, the patch should be fine.

-- 
Regards,
Igor.

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

* [PATCH 2/4] MTD: pxa3xx_nand: sperate each chip individual info
@ 2011-07-06  7:29                 ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-06  7:29 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lei,


You are going to resubmit the series to merge patches 3 and 4, so

a small nitpick below


On 07/04/11 12:25, Lei Wen wrote:

> For support two chip select, we seperate chip specific info in this
> patch.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  drivers/mtd/nand/pxa3xx_nand.c |  281 ++++++++++++++++++++++------------------
>  1 files changed, 154 insertions(+), 127 deletions(-)

[...]

>  
>  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>  {
> +	struct pxa3xx_nand_host *host = info->host;
>  	uint32_t ndcr = nand_readl(info, NDCR);
> -	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
> -	/* set info fields needed to read id */
> -	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
> -	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> -	info->cmdset = &default_cmdset;
>  
> -	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> -	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
> +	if (ndcr & NDCR_PAGE_SZ) {
> +		host->page_size = 2048;
> +		host->read_id_bytes = 4;
> +	} else {
> +		host->page_size = 512;
> +		host->read_id_bytes = 2;
> +	}

empty line would be nice here

> +	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
> +	host->cmdset = &default_cmdset;
> +
> +	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> +	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
>  
>  	return 0;
>  }

[...]

>  
> @@ -933,14 +947,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  	}
>  
>  	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
> -		kfree(mtd);
> -		info->mtd = NULL;
> +		host->mtd = NULL;
>  		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
>  
>  		return -EINVAL;
>  	}
>  
> -	pxa3xx_nand_config_flash(info, f);
> +	ret = pxa3xx_nand_config_flash(info, f);
> +	if (ret) {
> +		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
> +		return ret;
> +	}

and here

>  	pxa3xx_flash_ids[0].name = f->name;
>  	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
>  	pxa3xx_flash_ids[0].pagesize = f->page_size;
> @@ -955,44 +972,52 @@ KEEP_CONFIG:
>  	if (nand_scan_ident(mtd, 1, def))
>  		return -ENODEV;
>  	/* calculate addressing information */
> -	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
> +	if (mtd->writesize >= 2048)
> +		host->col_addr_cycles = 2;
> +	else
> +		host->col_addr_cycles = 1;

and here

>  	info->oob_buff = info->data_buff + mtd->writesize;
>  	if ((mtd->size >> chip->page_shift) > 65536)
> -		info->row_addr_cycles = 3;
> +		host->row_addr_cycles = 3;
>  	else
> -		info->row_addr_cycles = 2;
> +		host->row_addr_cycles = 2;

also here

>  	mtd->name = mtd_names[0];
>  	chip->ecc.mode = NAND_ECC_HW;
> -	chip->ecc.size = info->page_size;
> +	chip->ecc.size = host->page_size;
>  
> -	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
> +	chip->options = 0;
> +	if (host->reg_ndcr & NDCR_DWIDTH_M)
> +		chip->options = NAND_BUSWIDTH_16;
>  	chip->options |= NAND_NO_AUTOINCR;
>  	chip->options |= NAND_NO_READRDY;

here, you can just reorder the bit operations, so you won't need to do options = 0.
And empty line after if, would be great.

>  
>  	return nand_scan_tail(mtd);
>  }
>  

[...]

>  static int pxa3xx_nand_remove(struct platform_device *pdev)
>  {
>  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> -	struct mtd_info *mtd = info->mtd;
>  	struct resource *r;
>  	int irq;
>  
> +	if (!info)
> +		return 0;

and here (empty line)

apart from that nitpicking, the patch should be fine.

-- 
Regards,
Igor.

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-04  9:25               ` Lei Wen
@ 2011-07-06  7:41                 ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-06  7:41 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Haojian Zhuang,
	Daniel Mack, linux-mtd, linux-arm-kernel

On 07/04/11 12:25, Lei Wen wrote:

>  #ifdef CONFIG_PM
> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>  {
>  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>  
> -	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
> -	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
> +	/*
> +	 * Directly set the chip select to a invalid value,
> +	 * then the driver would reset the timing according
> +	 * to current chip select at the beginning of cmdfunc
> +	 */
> +	info->cs = 0xff;

Thinking of this for the second (or third) time,
If you have keep config enabled and have only one nand chip,
this will brake the keep config...

Daniel,

have you tested the suspend/resume with this patch?
(and keep_config on?)


-- 
Regards,
Igor.

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-06  7:41                 ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-06  7:41 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/04/11 12:25, Lei Wen wrote:

>  #ifdef CONFIG_PM
> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>  {
>  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>  
> -	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
> -	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
> +	/*
> +	 * Directly set the chip select to a invalid value,
> +	 * then the driver would reset the timing according
> +	 * to current chip select at the beginning of cmdfunc
> +	 */
> +	info->cs = 0xff;

Thinking of this for the second (or third) time,
If you have keep config enabled and have only one nand chip,
this will brake the keep config...

Daniel,

have you tested the suspend/resume with this patch?
(and keep_config on?)


-- 
Regards,
Igor.

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-06  7:41                 ` Igor Grinberg
@ 2011-07-07  6:26                   ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-07  6:26 UTC (permalink / raw)
  To: Igor Grinberg, Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen, Yu Tang,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

Hi Igor && Daniel,

On Wed, Jul 6, 2011 at 3:41 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/04/11 12:25, Lei Wen wrote:
>
>>  #ifdef CONFIG_PM
>> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>  {
>>       struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>
>> -     nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
>> -     nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
>> +     /*
>> +      * Directly set the chip select to a invalid value,
>> +      * then the driver would reset the timing according
>> +      * to current chip select at the beginning of cmdfunc
>> +      */
>> +     info->cs = 0xff;
>
> Thinking of this for the second (or third) time,
> If you have keep config enabled and have only one nand chip,
> this will brake the keep config...
>
> Daniel,
>
> have you tested the suspend/resume with this patch?
> (and keep_config on?)
>

Do you still have concern with this change?
If not, I would push the next round of patch set including merging
patch 3 and 4.

Thanks,
Lei

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-07  6:26                   ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-07  6:26 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Igor && Daniel,

On Wed, Jul 6, 2011 at 3:41 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/04/11 12:25, Lei Wen wrote:
>
>> ?#ifdef CONFIG_PM
>> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>
>> - ? ? nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
>> - ? ? nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
>> + ? ? /*
>> + ? ? ?* Directly set the chip select to a invalid value,
>> + ? ? ?* then the driver would reset the timing according
>> + ? ? ?* to current chip select at the beginning of cmdfunc
>> + ? ? ?*/
>> + ? ? info->cs = 0xff;
>
> Thinking of this for the second (or third) time,
> If you have keep config enabled and have only one nand chip,
> this will brake the keep config...
>
> Daniel,
>
> have you tested the suspend/resume with this patch?
> (and keep_config on?)
>

Do you still have concern with this change?
If not, I would push the next round of patch set including merging
patch 3 and 4.

Thanks,
Lei

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-07  6:26                   ` Lei Wen
@ 2011-07-07  8:59                     ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-07  8:59 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen, Yu Tang,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

On 07/07/11 09:26, Lei Wen wrote:

> Hi Igor && Daniel,
>
> On Wed, Jul 6, 2011 at 3:41 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 07/04/11 12:25, Lei Wen wrote:
>>
>>>  #ifdef CONFIG_PM
>>> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>  {
>>>       struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>
>>> -     nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
>>> -     nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
>>> +     /*
>>> +      * Directly set the chip select to a invalid value,
>>> +      * then the driver would reset the timing according
>>> +      * to current chip select at the beginning of cmdfunc
>>> +      */
>>> +     info->cs = 0xff;
>> Thinking of this for the second (or third) time,
>> If you have keep config enabled and have only one nand chip,
>> this will brake the keep config...
>>
>> Daniel,
>>
>> have you tested the suspend/resume with this patch?
>> (and keep_config on?)
>>
> Do you still have concern with this change?
> If not, I would push the next round of patch set including merging
> patch 3 and 4.

Though I can't test it right now, but yes it looks like it breaks
the keep_config after the resume (actually it disables the keep_config silently).

I think you need here some kind of check if keep_config is enabled.
keep_config is enabled means that only one chip select is used for NAND
and you don't need to reset the timings.


-- 
Regards,
Igor.

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-07  8:59                     ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-07  8:59 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/07/11 09:26, Lei Wen wrote:

> Hi Igor && Daniel,
>
> On Wed, Jul 6, 2011 at 3:41 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 07/04/11 12:25, Lei Wen wrote:
>>
>>>  #ifdef CONFIG_PM
>>> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>  {
>>>       struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>
>>> -     nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
>>> -     nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
>>> +     /*
>>> +      * Directly set the chip select to a invalid value,
>>> +      * then the driver would reset the timing according
>>> +      * to current chip select at the beginning of cmdfunc
>>> +      */
>>> +     info->cs = 0xff;
>> Thinking of this for the second (or third) time,
>> If you have keep config enabled and have only one nand chip,
>> this will brake the keep config...
>>
>> Daniel,
>>
>> have you tested the suspend/resume with this patch?
>> (and keep_config on?)
>>
> Do you still have concern with this change?
> If not, I would push the next round of patch set including merging
> patch 3 and 4.

Though I can't test it right now, but yes it looks like it breaks
the keep_config after the resume (actually it disables the keep_config silently).

I think you need here some kind of check if keep_config is enabled.
keep_config is enabled means that only one chip select is used for NAND
and you don't need to reset the timings.


-- 
Regards,
Igor.

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-07  8:59                     ` Igor Grinberg
@ 2011-07-07  9:06                       ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-07  9:06 UTC (permalink / raw)
  To: Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen, Yu Tang,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

Hi Igor,

On Thu, Jul 7, 2011 at 4:59 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/07/11 09:26, Lei Wen wrote:
>
>> Hi Igor && Daniel,
>>
>> On Wed, Jul 6, 2011 at 3:41 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>>> On 07/04/11 12:25, Lei Wen wrote:
>>>
>>>>  #ifdef CONFIG_PM
>>>> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>>  {
>>>>       struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>>
>>>> -     nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
>>>> -     nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
>>>> +     /*
>>>> +      * Directly set the chip select to a invalid value,
>>>> +      * then the driver would reset the timing according
>>>> +      * to current chip select at the beginning of cmdfunc
>>>> +      */
>>>> +     info->cs = 0xff;
>>> Thinking of this for the second (or third) time,
>>> If you have keep config enabled and have only one nand chip,
>>> this will brake the keep config...
>>>
>>> Daniel,
>>>
>>> have you tested the suspend/resume with this patch?
>>> (and keep_config on?)
>>>
>> Do you still have concern with this change?
>> If not, I would push the next round of patch set including merging
>> patch 3 and 4.
>
> Though I can't test it right now, but yes it looks like it breaks
> the keep_config after the resume (actually it disables the keep_config silently).
>
> I think you need here some kind of check if keep_config is enabled.
> keep_config is enabled means that only one chip select is used for NAND
> and you don't need to reset the timings.

It would not break anything.
The value rewrite to timing register is the one that save in the
pxa3xx_nand_detect_config.
And it is the same behavior before this patch apply.

Best regards,
Lei

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-07  9:06                       ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-07  9:06 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Igor,

On Thu, Jul 7, 2011 at 4:59 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/07/11 09:26, Lei Wen wrote:
>
>> Hi Igor && Daniel,
>>
>> On Wed, Jul 6, 2011 at 3:41 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>>> On 07/04/11 12:25, Lei Wen wrote:
>>>
>>>> ?#ifdef CONFIG_PM
>>>> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>> ?{
>>>> ? ? ? struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>>
>>>> - ? ? nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
>>>> - ? ? nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
>>>> + ? ? /*
>>>> + ? ? ?* Directly set the chip select to a invalid value,
>>>> + ? ? ?* then the driver would reset the timing according
>>>> + ? ? ?* to current chip select at the beginning of cmdfunc
>>>> + ? ? ?*/
>>>> + ? ? info->cs = 0xff;
>>> Thinking of this for the second (or third) time,
>>> If you have keep config enabled and have only one nand chip,
>>> this will brake the keep config...
>>>
>>> Daniel,
>>>
>>> have you tested the suspend/resume with this patch?
>>> (and keep_config on?)
>>>
>> Do you still have concern with this change?
>> If not, I would push the next round of patch set including merging
>> patch 3 and 4.
>
> Though I can't test it right now, but yes it looks like it breaks
> the keep_config after the resume (actually it disables the keep_config silently).
>
> I think you need here some kind of check if keep_config is enabled.
> keep_config is enabled means that only one chip select is used for NAND
> and you don't need to reset the timings.

It would not break anything.
The value rewrite to timing register is the one that save in the
pxa3xx_nand_detect_config.
And it is the same behavior before this patch apply.

Best regards,
Lei

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-07  9:06                       ` Lei Wen
@ 2011-07-07 11:23                         ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-07 11:23 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen, Yu Tang,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel

On 07/07/11 12:06, Lei Wen wrote:

> Hi Igor,
>
> On Thu, Jul 7, 2011 at 4:59 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 07/07/11 09:26, Lei Wen wrote:
>>
>>> Hi Igor && Daniel,
>>>
>>> On Wed, Jul 6, 2011 at 3:41 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>>>> On 07/04/11 12:25, Lei Wen wrote:
>>>>
>>>>>  #ifdef CONFIG_PM
>>>>> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>>>  {
>>>>>       struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>>>
>>>>> -     nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
>>>>> -     nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
>>>>> +     /*
>>>>> +      * Directly set the chip select to a invalid value,
>>>>> +      * then the driver would reset the timing according
>>>>> +      * to current chip select at the beginning of cmdfunc
>>>>> +      */
>>>>> +     info->cs = 0xff;
>>>> Thinking of this for the second (or third) time,
>>>> If you have keep config enabled and have only one nand chip,
>>>> this will brake the keep config...
>>>>
>>>> Daniel,
>>>>
>>>> have you tested the suspend/resume with this patch?
>>>> (and keep_config on?)
>>>>
>>> Do you still have concern with this change?
>>> If not, I would push the next round of patch set including merging
>>> patch 3 and 4.
>> Though I can't test it right now, but yes it looks like it breaks
>> the keep_config after the resume (actually it disables the keep_config silently).
>>
>> I think you need here some kind of check if keep_config is enabled.
>> keep_config is enabled means that only one chip select is used for NAND
>> and you don't need to reset the timings.
> It would not break anything.
> The value rewrite to timing register is the one that save in the
> pxa3xx_nand_detect_config.
> And it is the same behavior before this patch apply.

Right. I've missed this. Thanks for the explanation.

-- 
Regards,
Igor.

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-07 11:23                         ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-07 11:23 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/07/11 12:06, Lei Wen wrote:

> Hi Igor,
>
> On Thu, Jul 7, 2011 at 4:59 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 07/07/11 09:26, Lei Wen wrote:
>>
>>> Hi Igor && Daniel,
>>>
>>> On Wed, Jul 6, 2011 at 3:41 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>>>> On 07/04/11 12:25, Lei Wen wrote:
>>>>
>>>>>  #ifdef CONFIG_PM
>>>>> @@ -1203,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>>>  {
>>>>>       struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>>>>>
>>>>> -     nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
>>>>> -     nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
>>>>> +     /*
>>>>> +      * Directly set the chip select to a invalid value,
>>>>> +      * then the driver would reset the timing according
>>>>> +      * to current chip select at the beginning of cmdfunc
>>>>> +      */
>>>>> +     info->cs = 0xff;
>>>> Thinking of this for the second (or third) time,
>>>> If you have keep config enabled and have only one nand chip,
>>>> this will brake the keep config...
>>>>
>>>> Daniel,
>>>>
>>>> have you tested the suspend/resume with this patch?
>>>> (and keep_config on?)
>>>>
>>> Do you still have concern with this change?
>>> If not, I would push the next round of patch set including merging
>>> patch 3 and 4.
>> Though I can't test it right now, but yes it looks like it breaks
>> the keep_config after the resume (actually it disables the keep_config silently).
>>
>> I think you need here some kind of check if keep_config is enabled.
>> keep_config is enabled means that only one chip select is used for NAND
>> and you don't need to reset the timings.
> It would not break anything.
> The value rewrite to timing register is the one that save in the
> pxa3xx_nand_detect_config.
> And it is the same behavior before this patch apply.

Right. I've missed this. Thanks for the explanation.

-- 
Regards,
Igor.

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

* [PATCH V5 0/3] pxa3xx_nand: add two chip select support
  2011-07-04  9:25               ` Lei Wen
@ 2011-07-08  3:34                 ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08  3:34 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel,
	Yu Tang

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

V5:
Merge platform change patch and two chip feature into one patch.
Clean some format issue.

Lei Wen (3):
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 ++-
 drivers/mtd/nand/pxa3xx_nand.c               |  419 ++++++++++++++++----------
 9 files changed, 298 insertions(+), 180 deletions(-)

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

* [PATCH V5 0/3] pxa3xx_nand: add two chip select support
@ 2011-07-08  3:34                 ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08  3:34 UTC (permalink / raw)
  To: linux-arm-kernel

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

V5:
Merge platform change patch and two chip feature into one patch.
Clean some format issue.

Lei Wen (3):
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 ++-
 drivers/mtd/nand/pxa3xx_nand.c               |  419 ++++++++++++++++----------
 9 files changed, 298 insertions(+), 180 deletions(-)

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

* [PATCH 1/3] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-07-04  9:25               ` Lei Wen
@ 2011-07-08  3:34                 ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08  3:34 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel,
	Yu Tang

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..043c0bb 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH 1/3] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-07-08  3:34                 ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08  3:34 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..043c0bb 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH 2/3] MTD: pxa3xx_nand: sperate each chip individual info
  2011-07-04  9:25               ` Lei Wen
@ 2011-07-08  3:34                 ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08  3:34 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel,
	Yu Tang

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  287 ++++++++++++++++++++++------------------
 1 files changed, 159 insertions(+), 128 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 043c0bb..d94896a 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
 
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
+
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,52 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +867,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +874,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +884,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +916,11 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
+		host->mtd = NULL;
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +928,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
+		host->mtd = NULL;
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -933,14 +948,18 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
+		host->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -955,44 +974,53 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
+
 	mtd->name = mtd_names[0];
 	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
+	chip->ecc.size = host->page_size;
 
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
-	chip->options |= NAND_NO_AUTOINCR;
+	chip->options = NAND_NO_AUTOINCR;
 	chip->options |= NAND_NO_READRDY;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
 
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1028,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1106,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1124,19 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
+
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1144,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1156,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1165,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1173,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1176,8 +1207,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 2/3] MTD: pxa3xx_nand: sperate each chip individual info
@ 2011-07-08  3:34                 ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08  3:34 UTC (permalink / raw)
  To: linux-arm-kernel

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  287 ++++++++++++++++++++++------------------
 1 files changed, 159 insertions(+), 128 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 043c0bb..d94896a 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
 
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
+
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,52 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +867,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +874,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +884,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +916,11 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
+		host->mtd = NULL;
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +928,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
+		host->mtd = NULL;
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -933,14 +948,18 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
+		host->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -955,44 +974,53 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
+
 	mtd->name = mtd_names[0];
 	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
+	chip->ecc.size = host->page_size;
 
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
-	chip->options |= NAND_NO_AUTOINCR;
+	chip->options = NAND_NO_AUTOINCR;
 	chip->options |= NAND_NO_READRDY;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
 
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1028,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1106,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1124,19 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
+
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1144,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1156,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1165,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1173,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1176,8 +1207,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 3/3] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-04  9:25               ` Lei Wen
@ 2011-07-08  3:34                 ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08  3:34 UTC (permalink / raw)
  To: Artem Bityutskiy, Igor Grinberg, Eric Miao, David Woodhouse,
	Haojian Zhuang, Daniel Mack, linux-mtd, linux-arm-kernel,
	Yu Tang

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  151 ++++++++++++++++++--------
 9 files changed, 146 insertions(+), 64 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index d94896a..ceabd51 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -884,9 +915,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -918,7 +949,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
 		host->mtd = NULL;
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs %d!\n",
+			 info->cs);
 
 		return ret;
 	}
@@ -999,41 +1031,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1131,12 +1169,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1156,7 +1196,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1165,7 +1206,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1180,14 +1221,28 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1207,8 +1262,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH 3/3] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-08  3:34                 ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08  3:34 UTC (permalink / raw)
  To: linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  151 ++++++++++++++++++--------
 9 files changed, 146 insertions(+), 64 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index d94896a..ceabd51 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -884,9 +915,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -918,7 +949,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
 		host->mtd = NULL;
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs %d!\n",
+			 info->cs);
 
 		return ret;
 	}
@@ -999,41 +1031,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1131,12 +1169,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1156,7 +1196,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1165,7 +1206,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1180,14 +1221,28 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1207,8 +1262,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-08 15:26     ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08 15:26 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Igor,

On Wed, Jun 22, 2011 at 9:45 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> Hi Lei,
>
>
> Some comments from a quick look...
>
>
> On 06/22/11 06:17, Lei Wen wrote:
>
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>
> You should _also_ put this comment inside the pxa3xx_nand.h
> may be even inside the pxa3xx_nand_platform_data structure,
> so people would not have to search the git log to find this problem.

How about just throw out a BUG() to force people cannot set keep_config
when supported cs num more than 1? Also appended with the comments above?

>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>> ?arch/arm/mach-mmp/aspenite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/cm-x300.c ? ? ? ? ? ? ? ? ?| ? ?5 +-
>> ?arch/arm/mach-pxa/colibri-pxa3xx.c ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/littleton.c ? ? ? ? ? ? ? ?| ? ?5 +-
>> ?arch/arm/mach-pxa/mxm8x10.c ? ? ? ? ? ? ? ? ?| ? ?9 +-
>> ?arch/arm/mach-pxa/raumfeld.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/zylonite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | ? ?8 +-
>> ?drivers/mtd/nand/pxa3xx_nand.c ? ? ? ? ? ? ? | ?735 +++++++++++++++-----------
>> ?9 files changed, 444 insertions(+), 338 deletions(-)
>>
>> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
>> index 06b5fa8..b6589d6 100644
>> --- a/arch/arm/mach-mmp/aspenite.c
>> +++ b/arch/arm/mach-mmp/aspenite.c
>> @@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
>>
>> ?static struct pxa3xx_nand_platform_data aspenite_nand_info = {
>> ? ? ? .enable_arbiter = 1,
>> - ? ? .parts ? ? ? ? ?= aspenite_nand_partitions,
>> - ? ? .nr_parts ? ? ? = ARRAY_SIZE(aspenite_nand_partitions),
>> + ? ? .cs_num = 1,
>> + ? ? .parts[0] ? ? ? = aspenite_nand_partitions,
>> + ? ? .nr_parts[0] ? ?= ARRAY_SIZE(aspenite_nand_partitions),
>> ?};
>>
>> ?static struct i2c_board_info aspenite_i2c_info[] __initdata = {
>> diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
>> index b2248e7..d67eb7b 100644
>> --- a/arch/arm/mach-pxa/cm-x300.c
>> +++ b/arch/arm/mach-pxa/cm-x300.c
>> @@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
>> ?static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
>> ? ? ? .enable_arbiter = 1,
>> ? ? ? .keep_config ? ?= 1,
>> - ? ? .parts ? ? ? ? ?= cm_x300_nand_partitions,
>> - ? ? .nr_parts ? ? ? = ARRAY_SIZE(cm_x300_nand_partitions),
>> + ? ? .cs_num ? ? ? ? = 1,
>> + ? ? .parts[0] ? ? ? = cm_x300_nand_partitions,
>> + ? ? .nr_parts[0] ? ?= ARRAY_SIZE(cm_x300_nand_partitions),
>> ?};
>>
>> ?static void __init cm_x300_init_nand(void)
>> diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
>> index 3f9be41..ff7a07b 100644
>> --- a/arch/arm/mach-pxa/colibri-pxa3xx.c
>> +++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
>> @@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
>> ?static struct pxa3xx_nand_platform_data colibri_nand_info = {
>> ? ? ? .enable_arbiter = 1,
>> ? ? ? .keep_config ? ?= 1,
>> - ? ? .parts ? ? ? ? ?= colibri_nand_partitions,
>> - ? ? .nr_parts ? ? ? = ARRAY_SIZE(colibri_nand_partitions),
>> + ? ? .cs_num ? ? ? ? = 1,
>> + ? ? .parts[0] ? ? ? = colibri_nand_partitions,
>> + ? ? .nr_parts[0] ? ?= ARRAY_SIZE(colibri_nand_partitions),
>> ?};
>>
>> ?void __init colibri_pxa3xx_init_nand(void)
>> diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
>> index e5e326d..6eaf852 100644
>> --- a/arch/arm/mach-pxa/littleton.c
>> +++ b/arch/arm/mach-pxa/littleton.c
>> @@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
>>
>> ?static struct pxa3xx_nand_platform_data littleton_nand_info = {
>> ? ? ? .enable_arbiter = 1,
>> - ? ? .parts ? ? ? ? ?= littleton_nand_partitions,
>> - ? ? .nr_parts ? ? ? = ARRAY_SIZE(littleton_nand_partitions),
>> + ? ? .cs_num ? ? ? ? = 1,
>> + ? ? .parts[0] ? ? ? = littleton_nand_partitions,
>> + ? ? .nr_parts[0] ? ?= ARRAY_SIZE(littleton_nand_partitions),
>> ?};
>>
>> ?static void __init littleton_init_nand(void)
>> diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
>> index b5a8fd3..e7ce135 100644
>> --- a/arch/arm/mach-pxa/mxm8x10.c
>> +++ b/arch/arm/mach-pxa/mxm8x10.c
>> @@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
>> ?};
>>
>> ?static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
>> - ? ? .enable_arbiter = 1,
>> - ? ? .keep_config = 1,
>> - ? ? .parts = mxm_8x10_nand_partitions,
>> - ? ? .nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
>> + ? ? .enable_arbiter = 1,
>> + ? ? .keep_config ? ?= 1,
>> + ? ? .cs_num ? ? ? ? = 1,
>> + ? ? .parts[0] ? ? ? = mxm_8x10_nand_partitions,
>> + ? ? .nr_parts[0] ? ?= ARRAY_SIZE(mxm_8x10_nand_partitions)
>> ?};
>>
>> ?static void __init mxm_8x10_nand_init(void)
>> diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
>> index d130f77..a54846d 100644
>> --- a/arch/arm/mach-pxa/raumfeld.c
>> +++ b/arch/arm/mach-pxa/raumfeld.c
>> @@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
>> ?static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
>> ? ? ? .enable_arbiter = 1,
>> ? ? ? .keep_config ? ?= 1,
>> - ? ? .parts ? ? ? ? ?= raumfeld_nand_partitions,
>> - ? ? .nr_parts ? ? ? = ARRAY_SIZE(raumfeld_nand_partitions),
>> + ? ? .cs_num ? ? ? ? = 1,
>> + ? ? .parts[0] ? ? ? = raumfeld_nand_partitions,
>> + ? ? .nr_parts[0] ? ?= ARRAY_SIZE(raumfeld_nand_partitions),
>> ?};
>>
>> ?/**
>> diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
>> index 5821185..ea4752a 100644
>> --- a/arch/arm/mach-pxa/zylonite.c
>> +++ b/arch/arm/mach-pxa/zylonite.c
>> @@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
>>
>> ?static struct pxa3xx_nand_platform_data zylonite_nand_info = {
>> ? ? ? .enable_arbiter = 1,
>> - ? ? .parts ? ? ? ? ?= zylonite_nand_partitions,
>> - ? ? .nr_parts ? ? ? = ARRAY_SIZE(zylonite_nand_partitions),
>> + ? ? .cs_num ? ? ? ? = 1,
>> + ? ? .parts[0] ? ? ? = zylonite_nand_partitions,
>> + ? ? .nr_parts[0] ? ?= ARRAY_SIZE(zylonite_nand_partitions),
>> ?};
>>
>> ?static void __init zylonite_init_nand(void)
>> diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>> index 442301f..34a3f52 100644
>> --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>> +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
>> @@ -41,6 +41,8 @@ struct pxa3xx_nand_flash {
>> ? ? ? struct pxa3xx_nand_timing *timing; ? ? ?/* NAND Flash timing */
>> ?};
>>
>> +/* The max num of chip select current support */
>
> /* The maximum number of chip selects currently supported */
>
>> +#define NUM_CHIP_SELECT ? ? ? ? ? ? ?(2)
>> ?struct pxa3xx_nand_platform_data {
>>
>> ? ? ? /* the data flash bus is shared between the Static Memory
>> @@ -52,8 +54,10 @@ struct pxa3xx_nand_platform_data {
>> ? ? ? /* allow platform code to keep OBM/bootloader defined NFC config */
>> ? ? ? int ? ? keep_config;
>>
>> - ? ? const struct mtd_partition ? ? ? ? ? ? ?*parts;
>> - ? ? unsigned int ? ? ? ? ? ? ? ? ? ? ? ? ? ?nr_parts;
>> + ? ? /* indicate how many chip select would be used for this platform */
>
> /* indicate how many chip selects will be used */
>
>> + ? ? int ? ? cs_num;
>
> This name is too confusing, I think even num_cs is better or cs_count?
> Also, may be align it with the others?

 I prefer the num_cs.

>
>> + ? ? 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;
>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
>> index 30689cc..259b8d5 100644
>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>> +++ b/drivers/mtd/nand/pxa3xx_nand.c
>> @@ -92,11 +92,13 @@
>> ?#define NDCB0_ADDR_CYC_SHIFT (16)
>>
>> ?/* macros for registers read/write */
>> -#define nand_writel(info, off, val) ?\
>> - ? ? __raw_writel((val), (info)->mmio_base + (off))
>> +#define nand_writel(nand, off, val) ?\
>> + ? ? __raw_writel((val), (nand)->mmio_base + (off))
>>
>> -#define nand_readl(info, off) ? ? ? ? ? ? ? ?\
>> - ? ? __raw_readl((info)->mmio_base + (off))
>> +#define nand_readl(nand, off) ? ? ? ? ? ? ? ?\
>> + ? ? __raw_readl((nand)->mmio_base + (off))
>> +#define get_mtd_by_info(info) ? ? ? ? ? ? ? ?\
>> + ? ? (struct mtd_info *)((void *)info - sizeof(struct mtd_info))
>>
>> ?/* error code and state */
>> ?enum {
>> @@ -110,6 +112,7 @@ enum {
>>
>> ?enum {
>> ? ? ? STATE_IDLE = 0,
>> + ? ? STATE_PREPARED,
>> ? ? ? STATE_CMD_HANDLE,
>> ? ? ? STATE_DMA_READING,
>> ? ? ? STATE_DMA_WRITING,
>> @@ -123,63 +126,63 @@ enum {
>> ?struct pxa3xx_nand_info {
>> ? ? ? struct nand_chip ? ? ? ?nand_chip;
>>
>> - ? ? struct nand_hw_control ?controller;
>> - ? ? struct platform_device ? *pdev;
>> ? ? ? struct pxa3xx_nand_cmdset *cmdset;
>> + ? ? /* page size of attached chip */
>> + ? ? uint16_t ? ? ? ? ? ? ? ?page_size;
>> + ? ? uint8_t ? ? ? ? ? ? ? ? chip_select;
>> + ? ? uint8_t ? ? ? ? ? ? ? ? use_ecc;
>> +
>> + ? ? /* calculated from pxa3xx_nand_flash data */
>> + ? ? uint8_t ? ? ? ? ? ? ? ? col_addr_cycles;
>> + ? ? uint8_t ? ? ? ? ? ? ? ? row_addr_cycles;
>> + ? ? uint8_t ? ? ? ? ? ? ? ? read_id_bytes;
>> +
>> + ? ? /* cached register value */
>> + ? ? uint32_t ? ? ? ? ? ? ? ?reg_ndcr;
>> + ? ? uint32_t ? ? ? ? ? ? ? ?ndtr0cs0;
>> + ? ? uint32_t ? ? ? ? ? ? ? ?ndtr1cs0;
>>
>> + ? ? void ? ? ? ? ? ? ? ? ? ?*nand_data;
>> +};
>> +
>> +struct pxa3xx_nand {
>> ? ? ? struct clk ? ? ? ? ? ? ?*clk;
>> ? ? ? void __iomem ? ? ? ? ? ?*mmio_base;
>> ? ? ? unsigned long ? ? ? ? ? mmio_phys;
>> + ? ? struct nand_hw_control ?controller;
>> + ? ? struct completion ? ? ? cmd_complete;
>> + ? ? struct platform_device ? *pdev;
>
> please, align

Do you still mean cs_num? What this comment refer to?

>
>>
>> - ? ? unsigned int ? ? ? ? ? ?buf_start;
>> - ? ? unsigned int ? ? ? ? ? ?buf_count;
>> -
>> - ? ? struct mtd_info ? ? ? ? *mtd;
>> ? ? ? /* DMA information */
>> ? ? ? int ? ? ? ? ? ? ? ? ? ? drcmr_dat;
>> ? ? ? int ? ? ? ? ? ? ? ? ? ? drcmr_cmd;
>> -
>> - ? ? unsigned char ? ? ? ? ? *data_buff;
>> - ? ? unsigned char ? ? ? ? ? *oob_buff;
>> - ? ? dma_addr_t ? ? ? ? ? ? ?data_buff_phys;
>> - ? ? size_t ? ? ? ? ? ? ? ? ?data_buff_size;
>> ? ? ? int ? ? ? ? ? ? ? ? ? ? data_dma_ch;
>> - ? ? struct pxa_dma_desc ? ? *data_desc;
>> + ? ? dma_addr_t ? ? ? ? ? ? ?data_buff_phys;
>> ? ? ? dma_addr_t ? ? ? ? ? ? ?data_desc_addr;
>> + ? ? struct pxa_dma_desc ? ? *data_desc;
>>
>> - ? ? uint32_t ? ? ? ? ? ? ? ?reg_ndcr;
>> -
>> - ? ? /* saved column/page_addr during CMD_SEQIN */
>> - ? ? int ? ? ? ? ? ? ? ? ? ? seqin_column;
>> - ? ? int ? ? ? ? ? ? ? ? ? ? seqin_page_addr;
>> + ? ? struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
>> + ? ? uint32_t ? ? ? ? ? ? ? ?command;
>> + ? ? uint16_t ? ? ? ? ? ? ? ?data_size; ? ? ?/* data size in FIFO */
>> + ? ? uint16_t ? ? ? ? ? ? ? ?oob_size;
>> + ? ? unsigned char ? ? ? ? ? *data_buff;
>> + ? ? unsigned char ? ? ? ? ? *oob_buff;
>> + ? ? uint32_t ? ? ? ? ? ? ? ?buf_start;
>> + ? ? uint32_t ? ? ? ? ? ? ? ?buf_count;
>>
>> ? ? ? /* relate to the command */
>> ? ? ? unsigned int ? ? ? ? ? ?state;
>> -
>> + ? ? uint8_t ? ? ? ? ? ? ? ? chip_select;
>> ? ? ? int ? ? ? ? ? ? ? ? ? ? use_ecc; ? ? ? ?/* use HW ECC ? */
>> ? ? ? int ? ? ? ? ? ? ? ? ? ? use_dma; ? ? ? ?/* use DMA ? */
>> ? ? ? int ? ? ? ? ? ? ? ? ? ? is_ready;
>> -
>> - ? ? unsigned int ? ? ? ? ? ?page_size; ? ? ?/* page size of attached chip */
>> - ? ? unsigned int ? ? ? ? ? ?data_size; ? ? ?/* data size in FIFO */
>> ? ? ? int ? ? ? ? ? ? ? ? ? ? retcode;
>> - ? ? struct completion ? ? ? cmd_complete;
>>
>> ? ? ? /* generated NDCBx register values */
>> + ? ? uint8_t ? ? ? ? ? ? ? ? total_cmds;
>> ? ? ? uint32_t ? ? ? ? ? ? ? ?ndcb0;
>> ? ? ? uint32_t ? ? ? ? ? ? ? ?ndcb1;
>> ? ? ? uint32_t ? ? ? ? ? ? ? ?ndcb2;
>> -
>> - ? ? /* timing calcuted from setting */
>> - ? ? uint32_t ? ? ? ? ? ? ? ?ndtr0cs0;
>> - ? ? uint32_t ? ? ? ? ? ? ? ?ndtr1cs0;
>> -
>> - ? ? /* calculated from pxa3xx_nand_flash data */
>> - ? ? size_t ? ? ? ? ?oob_size;
>> - ? ? size_t ? ? ? ? ?read_id_bytes;
>> -
>> - ? ? unsigned int ? ?col_addr_cycles;
>> - ? ? unsigned int ? ?row_addr_cycles;
>> ?};
>
> It looks like if you switch the names of the structures above,
> then the patch will be much shorter and cleaner,
> but will it make structures meaning confusion?

Could you elaborate more about this? Do you means no need to create a seperated
structure?

>
>>
>> ?static int use_dma = 1;
>> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>> ?/* Define a default flash type setting serve as flash detecting only */
>> ?#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>>
>> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>>
>> ?#define NDTR0_tCH(c) (min((c), 7) << 19)
>> ?#define NDTR0_tCS(c) (min((c), 7) << 16)
>> @@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>> ?static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?const struct pxa3xx_nand_timing *t)
>> ?{
>> - ? ? unsigned long nand_clk = clk_get_rate(info->clk);
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? unsigned long nand_clk;
>> ? ? ? uint32_t ndtr0, ndtr1;
>>
>> + ? ? nand_clk = clk_get_rate(nand->clk);
>> ? ? ? ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
>> ? ? ? ? ? ? ? NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
>> ? ? ? ? ? ? ? NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
>> @@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>>
>> ? ? ? info->ndtr0cs0 = ndtr0;
>> ? ? ? info->ndtr1cs0 = ndtr1;
>> - ? ? nand_writel(info, NDTR0CS0, ndtr0);
>> - ? ? nand_writel(info, NDTR1CS0, ndtr1);
>> + ? ? nand_writel(nand, NDTR0CS0, ndtr0);
>> + ? ? nand_writel(nand, NDTR1CS0, ndtr1);
>> ?}
>>
>> ?static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>> ?{
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> ? ? ? int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
>>
>> - ? ? info->data_size = info->page_size;
>> + ? ? nand->data_size = info->page_size;
>> ? ? ? if (!oob_enable) {
>> - ? ? ? ? ? ? info->oob_size = 0;
>> + ? ? ? ? ? ? nand->oob_size = 0;
>> ? ? ? ? ? ? ? return;
>> ? ? ? }
>>
>> ? ? ? switch (info->page_size) {
>> ? ? ? case 2048:
>> - ? ? ? ? ? ? info->oob_size = (info->use_ecc) ? 40 : 64;
>> + ? ? ? ? ? ? nand->oob_size = (info->use_ecc) ? 40 : 64;
>> ? ? ? ? ? ? ? break;
>> ? ? ? case 512:
>> - ? ? ? ? ? ? info->oob_size = (info->use_ecc) ? 8 : 16;
>> + ? ? ? ? ? ? nand->oob_size = (info->use_ecc) ? 8 : 16;
>> ? ? ? ? ? ? ? break;
>> ? ? ? }
>> ?}
>> @@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>> ? * 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_info *info)
>> +static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
>> ?{
>> + ? ? struct pxa3xx_nand_info *info;
>> ? ? ? uint32_t ndcr;
>>
>> + ? ? info = nand->info[nand->chip_select];
>> ? ? ? ndcr = info->reg_ndcr;
>> - ? ? ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
>> - ? ? ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
>> + ? ? ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
>> + ? ? ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
>> ? ? ? ndcr |= NDCR_ND_RUN;
>>
>> ? ? ? /* clear status bits and run */
>> - ? ? nand_writel(info, NDCR, 0);
>> - ? ? nand_writel(info, NDSR, NDSR_MASK);
>> - ? ? nand_writel(info, NDCR, ndcr);
>> + ? ? nand_writel(nand, NDCR, 0);
>> + ? ? nand_writel(nand, NDSR, NDSR_MASK);
>> + ? ? nand_writel(nand, NDCR, ndcr);
>> ?}
>>
>> -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
>> +static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
>> ?{
>> ? ? ? uint32_t ndcr;
>> ? ? ? int timeout = NAND_STOP_DELAY;
>>
>> ? ? ? /* wait RUN bit in NDCR become 0 */
>> - ? ? ndcr = nand_readl(info, NDCR);
>> + ? ? ndcr = nand_readl(nand, NDCR);
>> ? ? ? while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
>> - ? ? ? ? ? ? ndcr = nand_readl(info, NDCR);
>> + ? ? ? ? ? ? ndcr = nand_readl(nand, NDCR);
>> ? ? ? ? ? ? ? udelay(1);
>> ? ? ? }
>>
>> ? ? ? if (timeout <= 0) {
>> ? ? ? ? ? ? ? ndcr &= ~NDCR_ND_RUN;
>> - ? ? ? ? ? ? nand_writel(info, NDCR, ndcr);
>> + ? ? ? ? ? ? nand_writel(nand, NDCR, ndcr);
>> ? ? ? }
>> ? ? ? /* clear status bits */
>> - ? ? nand_writel(info, NDSR, NDSR_MASK);
>> + ? ? nand_writel(nand, NDSR, NDSR_MASK);
>> ?}
>>
>> -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>> +static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>> ?{
>> ? ? ? uint32_t ndcr;
>>
>> - ? ? ndcr = nand_readl(info, NDCR);
>> - ? ? nand_writel(info, NDCR, ndcr & ~int_mask);
>> + ? ? ndcr = nand_readl(nand, NDCR);
>> + ? ? nand_writel(nand, NDCR, ndcr & ~int_mask);
>> ?}
>>
>> -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>> +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>> ?{
>> ? ? ? uint32_t ndcr;
>>
>> - ? ? ndcr = nand_readl(info, NDCR);
>> - ? ? nand_writel(info, NDCR, ndcr | int_mask);
>> + ? ? ndcr = nand_readl(nand, NDCR);
>> + ? ? nand_writel(nand, NDCR, ndcr | int_mask);
>> ?}
>>
>> -static void handle_data_pio(struct pxa3xx_nand_info *info)
>> +static void handle_data_pio(struct pxa3xx_nand *nand)
>> ?{
>> - ? ? switch (info->state) {
>> + ? ? switch (nand->state) {
>> ? ? ? case STATE_PIO_WRITING:
>> - ? ? ? ? ? ? __raw_writesl(info->mmio_base + NDDB, info->data_buff,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->data_size, 4));
>> - ? ? ? ? ? ? if (info->oob_size > 0)
>> - ? ? ? ? ? ? ? ? ? ? __raw_writesl(info->mmio_base + NDDB, info->oob_buff,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->oob_size, 4));
>> + ? ? ? ? ? ? __raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->data_size, 4));
>> + ? ? ? ? ? ? if (nand->oob_size > 0)
>> + ? ? ? ? ? ? ? ? ? ? __raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->oob_size, 4));
>> ? ? ? ? ? ? ? break;
>> ? ? ? case STATE_PIO_READING:
>> - ? ? ? ? ? ? __raw_readsl(info->mmio_base + NDDB, info->data_buff,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->data_size, 4));
>> - ? ? ? ? ? ? if (info->oob_size > 0)
>> - ? ? ? ? ? ? ? ? ? ? __raw_readsl(info->mmio_base + NDDB, info->oob_buff,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->oob_size, 4));
>> + ? ? ? ? ? ? __raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->data_size, 4));
>> + ? ? ? ? ? ? if (nand->oob_size > 0)
>> + ? ? ? ? ? ? ? ? ? ? __raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->oob_size, 4));
>> ? ? ? ? ? ? ? break;
>> ? ? ? default:
>> ? ? ? ? ? ? ? printk(KERN_ERR "%s: invalid state %d\n", __func__,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->state);
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->state);
>> ? ? ? ? ? ? ? BUG();
>> ? ? ? }
>> ?}
>>
>> -static void start_data_dma(struct pxa3xx_nand_info *info)
>> +static void start_data_dma(struct pxa3xx_nand *nand)
>> ?{
>> - ? ? struct pxa_dma_desc *desc = info->data_desc;
>> - ? ? int dma_len = ALIGN(info->data_size + info->oob_size, 32);
>> + ? ? struct pxa_dma_desc *desc = nand->data_desc;
>> + ? ? int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
>>
>> ? ? ? desc->ddadr = DDADR_STOP;
>> ? ? ? desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
>>
>> - ? ? switch (info->state) {
>> + ? ? switch (nand->state) {
>> ? ? ? case STATE_DMA_WRITING:
>> - ? ? ? ? ? ? desc->dsadr = info->data_buff_phys;
>> - ? ? ? ? ? ? desc->dtadr = info->mmio_phys + NDDB;
>> + ? ? ? ? ? ? desc->dsadr = nand->data_buff_phys;
>> + ? ? ? ? ? ? desc->dtadr = nand->mmio_phys + NDDB;
>> ? ? ? ? ? ? ? desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
>> ? ? ? ? ? ? ? break;
>> ? ? ? case STATE_DMA_READING:
>> - ? ? ? ? ? ? desc->dtadr = info->data_buff_phys;
>> - ? ? ? ? ? ? desc->dsadr = info->mmio_phys + NDDB;
>> + ? ? ? ? ? ? desc->dtadr = nand->data_buff_phys;
>> + ? ? ? ? ? ? desc->dsadr = nand->mmio_phys + NDDB;
>> ? ? ? ? ? ? ? desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
>> ? ? ? ? ? ? ? break;
>> ? ? ? default:
>> ? ? ? ? ? ? ? printk(KERN_ERR "%s: invalid state %d\n", __func__,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->state);
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->state);
>> ? ? ? ? ? ? ? BUG();
>> ? ? ? }
>>
>> - ? ? DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
>> - ? ? DDADR(info->data_dma_ch) = info->data_desc_addr;
>> - ? ? DCSR(info->data_dma_ch) |= DCSR_RUN;
>> + ? ? DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
>> + ? ? DDADR(nand->data_dma_ch) = nand->data_desc_addr;
>> + ? ? DCSR(nand->data_dma_ch) |= DCSR_RUN;
>> ?}
>>
>> ?static void pxa3xx_nand_data_dma_irq(int channel, void *data)
>> ?{
>> - ? ? struct pxa3xx_nand_info *info = data;
>> + ? ? struct pxa3xx_nand *nand = data;
>> ? ? ? uint32_t dcsr;
>>
>> ? ? ? dcsr = DCSR(channel);
>> ? ? ? DCSR(channel) = dcsr;
>>
>> ? ? ? if (dcsr & DCSR_BUSERR) {
>> - ? ? ? ? ? ? info->retcode = ERR_DMABUSERR;
>> + ? ? ? ? ? ? nand->retcode = ERR_DMABUSERR;
>> ? ? ? }
>>
>> - ? ? info->state = STATE_DMA_DONE;
>> - ? ? enable_int(info, NDCR_INT_MASK);
>> - ? ? nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>> + ? ? nand->state = STATE_DMA_DONE;
>> + ? ? enable_int(nand, NDCR_INT_MASK);
>> + ? ? nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>> ?}
>>
>> ?static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>> ?{
>> - ? ? struct pxa3xx_nand_info *info = devid;
>> - ? ? unsigned int status, is_completed = 0;
>> + ? ? struct pxa3xx_nand *nand = devid;
>> + ? ? struct pxa3xx_nand_info *info;
>> + ? ? unsigned int status, is_completed = 0, cs;
>> + ? ? unsigned int ready, cmd_done, page_done, badblock_detect;
>>
>> - ? ? status = nand_readl(info, NDSR);
>> + ? ? cs ? ? ? ? ? ? ?= nand->chip_select;
>> + ? ? ready ? ? ? ? ? = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
>> + ? ? cmd_done ? ? ? ?= (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
>> + ? ? page_done ? ? ? = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
>> + ? ? badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
>
> This is confusing... do you use to ?: operator for differentiating between
> cs = 0 and cs = 1?
> I think this is a bad idea.
> Moreover, the use of ?: is discouraged among the kernel.

I could use if/else to replace this statement.

>
>> + ? ? info ? ? ? ? ? ?= nand->info[cs];
>>
>> + ? ? status = nand_readl(nand, NDSR);
>> ? ? ? if (status & NDSR_DBERR)
>> - ? ? ? ? ? ? info->retcode = ERR_DBERR;
>> + ? ? ? ? ? ? nand->retcode = ERR_DBERR;
>> ? ? ? if (status & NDSR_SBERR)
>> - ? ? ? ? ? ? info->retcode = ERR_SBERR;
>> + ? ? ? ? ? ? nand->retcode = ERR_SBERR;
>> ? ? ? if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
>> ? ? ? ? ? ? ? /* whether use dma to transfer data */
>> - ? ? ? ? ? ? if (info->use_dma) {
>> - ? ? ? ? ? ? ? ? ? ? disable_int(info, NDCR_INT_MASK);
>> - ? ? ? ? ? ? ? ? ? ? info->state = (status & NDSR_RDDREQ) ?
>> + ? ? ? ? ? ? if (nand->use_dma) {
>> + ? ? ? ? ? ? ? ? ? ? disable_int(nand, NDCR_INT_MASK);
>> + ? ? ? ? ? ? ? ? ? ? nand->state = (status & NDSR_RDDREQ) ?
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? STATE_DMA_READING : STATE_DMA_WRITING;
>> - ? ? ? ? ? ? ? ? ? ? start_data_dma(info);
>> + ? ? ? ? ? ? ? ? ? ? start_data_dma(nand);
>> ? ? ? ? ? ? ? ? ? ? ? goto NORMAL_IRQ_EXIT;
>> ? ? ? ? ? ? ? } else {
>> - ? ? ? ? ? ? ? ? ? ? info->state = (status & NDSR_RDDREQ) ?
>> + ? ? ? ? ? ? ? ? ? ? nand->state = (status & NDSR_RDDREQ) ?
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? STATE_PIO_READING : STATE_PIO_WRITING;
>> - ? ? ? ? ? ? ? ? ? ? handle_data_pio(info);
>> + ? ? ? ? ? ? ? ? ? ? handle_data_pio(nand);
>> ? ? ? ? ? ? ? }
>> ? ? ? }
>> - ? ? if (status & NDSR_CS0_CMDD) {
>> - ? ? ? ? ? ? info->state = STATE_CMD_DONE;
>> + ? ? if (status & cmd_done) {
>> + ? ? ? ? ? ? nand->state = STATE_CMD_DONE;
>> ? ? ? ? ? ? ? is_completed = 1;
>> ? ? ? }
>> - ? ? if (status & NDSR_FLASH_RDY) {
>> - ? ? ? ? ? ? info->is_ready = 1;
>> - ? ? ? ? ? ? info->state = STATE_READY;
>> + ? ? if (status & ready) {
>> + ? ? ? ? ? ? nand->is_ready = 1;
>> + ? ? ? ? ? ? nand->state = STATE_READY;
>> ? ? ? }
>>
>> ? ? ? if (status & NDSR_WRCMDREQ) {
>> - ? ? ? ? ? ? nand_writel(info, NDSR, NDSR_WRCMDREQ);
>> + ? ? ? ? ? ? nand_writel(nand, NDSR, NDSR_WRCMDREQ);
>> ? ? ? ? ? ? ? status &= ~NDSR_WRCMDREQ;
>> - ? ? ? ? ? ? info->state = STATE_CMD_HANDLE;
>> - ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb0);
>> - ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb1);
>> - ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb2);
>> + ? ? ? ? ? ? nand->state = STATE_CMD_HANDLE;
>> + ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb0);
>> + ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb1);
>> + ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb2);
>> ? ? ? }
>>
>> ? ? ? /* clear NDSR to let the controller exit the IRQ */
>> - ? ? nand_writel(info, NDSR, status);
>> + ? ? nand_writel(nand, NDSR, status);
>> ? ? ? if (is_completed)
>> - ? ? ? ? ? ? complete(&info->cmd_complete);
>> + ? ? ? ? ? ? complete(&nand->cmd_complete);
>> ?NORMAL_IRQ_EXIT:
>> ? ? ? return IRQ_HANDLED;
>> ?}
>>
>> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
>> -{
>> - ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> - ? ? return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
>> -}
>> -
>> ?static inline int is_buf_blank(uint8_t *buf, size_t len)
>> ?{
>> ? ? ? for (; len > 0; len--)
>> @@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
>> ? ? ? return 1;
>> ?}
>>
>> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>> ? ? ? ? ? ? ? uint16_t column, int page_addr)
>> ?{
>> ? ? ? uint16_t cmd;
>> ? ? ? int addr_cycle, exec_cmd, ndcb0;
>> - ? ? struct mtd_info *mtd = info->mtd;
>> + ? ? struct mtd_info *mtd;
>> + ? ? struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>>
>> - ? ? ndcb0 = 0;
>> + ? ? mtd = get_mtd_by_info(info);
>> + ? ? ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
>
> This one is confusing too...
> Besides, you don't need the parenthesis.

Got that. I would fix it.

>
>> ? ? ? addr_cycle = 0;
>> ? ? ? exec_cmd = 1;
>>
>> ? ? ? /* reset data and oob column point to handle data */
>> - ? ? info->buf_start ? ? ? ? = 0;
>> - ? ? info->buf_count ? ? ? ? = 0;
>> - ? ? info->oob_size ? ? ? ? ?= 0;
>> - ? ? info->use_ecc ? ? ? ? ? = 0;
>> - ? ? info->is_ready ? ? ? ? ?= 0;
>> - ? ? info->retcode ? ? ? ? ? = ERR_NONE;
>> + ? ? nand->buf_start ? ? ? ? = 0;
>> + ? ? nand->buf_count ? ? ? ? = 0;
>> + ? ? nand->oob_size ? ? ? ? ?= 0;
>> + ? ? nand->use_ecc ? ? ? ? ? = 0;
>> + ? ? nand->is_ready ? ? ? ? ?= 0;
>> + ? ? nand->retcode ? ? ? ? ? = ERR_NONE;
>> + ? ? nand->data_size ? ? ? ? = 0;
>> + ? ? nand->use_dma ? ? ? ? ? = 0;
>> + ? ? nand->command ? ? ? ? ? = command;
>>
>> ? ? ? switch (command) {
>> ? ? ? case NAND_CMD_READ0:
>> ? ? ? case NAND_CMD_PAGEPROG:
>> - ? ? ? ? ? ? info->use_ecc = 1;
>> + ? ? ? ? ? ? nand->use_ecc = 1;
>> ? ? ? case NAND_CMD_READOOB:
>> ? ? ? ? ? ? ? pxa3xx_set_datasize(info);
>> + ? ? ? ? ? ? nand->oob_buff = nand->data_buff + nand->data_size;
>> + ? ? ? ? ? ? nand->use_dma = use_dma;
>> ? ? ? ? ? ? ? break;
>> ? ? ? case NAND_CMD_SEQIN:
>> ? ? ? ? ? ? ? exec_cmd = 0;
>> ? ? ? ? ? ? ? break;
>> ? ? ? default:
>> - ? ? ? ? ? ? info->ndcb1 = 0;
>> - ? ? ? ? ? ? info->ndcb2 = 0;
>> + ? ? ? ? ? ? nand->ndcb1 = 0;
>> + ? ? ? ? ? ? nand->ndcb2 = 0;
>> ? ? ? ? ? ? ? break;
>> ? ? ? }
>>
>> - ? ? info->ndcb0 = ndcb0;
>> + ? ? nand->ndcb0 = ndcb0;
>> ? ? ? addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? + info->col_addr_cycles);
>>
>> @@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>> ? ? ? case NAND_CMD_READ0:
>> ? ? ? ? ? ? ? cmd = info->cmdset->read1;
>> ? ? ? ? ? ? ? if (command == NAND_CMD_READOOB)
>> - ? ? ? ? ? ? ? ? ? ? info->buf_start = mtd->writesize + column;
>> + ? ? ? ? ? ? ? ? ? ? nand->buf_start = mtd->writesize + column;
>> ? ? ? ? ? ? ? else
>> - ? ? ? ? ? ? ? ? ? ? info->buf_start = column;
>> + ? ? ? ? ? ? ? ? ? ? nand->buf_start = column;
>>
>> ? ? ? ? ? ? ? if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
>> - ? ? ? ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0)
>> + ? ? ? ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | addr_cycle
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | (cmd & NDCB0_CMD1_MASK);
>> ? ? ? ? ? ? ? else
>> - ? ? ? ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0)
>> + ? ? ? ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_DBC
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | addr_cycle
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | cmd;
>> @@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>> ? ? ? case NAND_CMD_SEQIN:
>> ? ? ? ? ? ? ? /* small page addr setting */
>> ? ? ? ? ? ? ? if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
>> - ? ? ? ? ? ? ? ? ? ? info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>> + ? ? ? ? ? ? ? ? ? ? nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | (column & 0xFF);
>>
>> - ? ? ? ? ? ? ? ? ? ? info->ndcb2 = 0;
>> + ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = 0;
>> ? ? ? ? ? ? ? } else {
>> - ? ? ? ? ? ? ? ? ? ? info->ndcb1 = ((page_addr & 0xFFFF) << 16)
>> + ? ? ? ? ? ? ? ? ? ? nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | (column & 0xFFFF);
>>
>> ? ? ? ? ? ? ? ? ? ? ? if (page_addr & 0xFF0000)
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->ndcb2 = (page_addr & 0xFF0000) >> 16;
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
>> ? ? ? ? ? ? ? ? ? ? ? else
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->ndcb2 = 0;
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = 0;
>> ? ? ? ? ? ? ? }
>>
>> - ? ? ? ? ? ? info->buf_count = mtd->writesize + mtd->oobsize;
>> - ? ? ? ? ? ? memset(info->data_buff, 0xFF, info->buf_count);
>> + ? ? ? ? ? ? nand->buf_count = mtd->writesize + mtd->oobsize;
>> + ? ? ? ? ? ? memset(nand->data_buff, 0xFF, nand->buf_count);
>>
>> ? ? ? ? ? ? ? break;
>>
>> ? ? ? case NAND_CMD_PAGEPROG:
>> - ? ? ? ? ? ? if (is_buf_blank(info->data_buff,
>> + ? ? ? ? ? ? if (is_buf_blank(nand->data_buff,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (mtd->writesize + mtd->oobsize))) {
>> ? ? ? ? ? ? ? ? ? ? ? exec_cmd = 0;
>> ? ? ? ? ? ? ? ? ? ? ? break;
>> ? ? ? ? ? ? ? }
>>
>> ? ? ? ? ? ? ? cmd = info->cmdset->program;
>> - ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>> + ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_AUTO_RS
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_ST_ROW_EN
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_DBC
>> @@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>
>> ? ? ? case NAND_CMD_READID:
>> ? ? ? ? ? ? ? cmd = info->cmdset->read_id;
>> - ? ? ? ? ? ? info->buf_count = info->read_id_bytes;
>> - ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(3)
>> + ? ? ? ? ? ? nand->buf_count = info->read_id_bytes;
>> + ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(3)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_ADDR_CYC(1)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | cmd;
>>
>> - ? ? ? ? ? ? info->data_size = 8;
>> + ? ? ? ? ? ? nand->data_size = 8;
>> ? ? ? ? ? ? ? break;
>> ? ? ? case NAND_CMD_STATUS:
>> ? ? ? ? ? ? ? cmd = info->cmdset->read_status;
>> - ? ? ? ? ? ? info->buf_count = 1;
>> - ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(4)
>> + ? ? ? ? ? ? nand->buf_count = 1;
>> + ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(4)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_ADDR_CYC(1)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | cmd;
>>
>> - ? ? ? ? ? ? info->data_size = 8;
>> + ? ? ? ? ? ? nand->data_size = 8;
>> ? ? ? ? ? ? ? break;
>>
>> ? ? ? case NAND_CMD_ERASE1:
>> ? ? ? ? ? ? ? cmd = info->cmdset->erase;
>> - ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(2)
>> + ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(2)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_AUTO_RS
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_ADDR_CYC(3)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | NDCB0_DBC
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | cmd;
>> - ? ? ? ? ? ? info->ndcb1 = page_addr;
>> - ? ? ? ? ? ? info->ndcb2 = 0;
>> + ? ? ? ? ? ? nand->ndcb1 = page_addr;
>> + ? ? ? ? ? ? nand->ndcb2 = 0;
>>
>> ? ? ? ? ? ? ? break;
>> ? ? ? case NAND_CMD_RESET:
>> ? ? ? ? ? ? ? cmd = info->cmdset->reset;
>> - ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(5)
>> + ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(5)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | cmd;
>>
>> ? ? ? ? ? ? ? break;
>> @@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int column, int page_addr)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> ? ? ? int ret, exec_cmd;
>>
>> ? ? ? /*
>> @@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>> ? ? ? if (info->reg_ndcr & NDCR_DWIDTH_M)
>> ? ? ? ? ? ? ? column /= 2;
>>
>> - ? ? exec_cmd = prepare_command_pool(info, command, column, page_addr);
>> + ? ? /*
>> + ? ? ?* There may be different NAND chip hooked to
>> + ? ? ?* different chip select, so check whether
>> + ? ? ?* chip select has been changed, if yes, reset the timing
>> + ? ? ?*/
>> + ? ? if (nand->chip_select != info->chip_select) {
>> + ? ? ? ? ? ? nand->chip_select = info->chip_select;
>> + ? ? ? ? ? ? nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>> + ? ? ? ? ? ? nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>> + ? ? }
>> +
>> + ? ? nand->state = STATE_PREPARED;
>> + ? ? exec_cmd = prepare_command_pool(nand, command, column, page_addr);
>> ? ? ? if (exec_cmd) {
>> - ? ? ? ? ? ? init_completion(&info->cmd_complete);
>> - ? ? ? ? ? ? pxa3xx_nand_start(info);
>> + ? ? ? ? ? ? init_completion(&nand->cmd_complete);
>> + ? ? ? ? ? ? pxa3xx_nand_start(nand);
>>
>> - ? ? ? ? ? ? ret = wait_for_completion_timeout(&info->cmd_complete,
>> + ? ? ? ? ? ? ret = wait_for_completion_timeout(&nand->cmd_complete,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CHIP_DELAY_TIMEOUT);
>> ? ? ? ? ? ? ? if (!ret) {
>> ? ? ? ? ? ? ? ? ? ? ? printk(KERN_ERR "Wait time out!!!\n");
>> ? ? ? ? ? ? ? ? ? ? ? /* Stop State Machine for next command cycle */
>> - ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_stop(info);
>> + ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_stop(nand);
>> ? ? ? ? ? ? ? }
>> - ? ? ? ? ? ? info->state = STATE_IDLE;
>> ? ? ? }
>> + ? ? nand->state = STATE_IDLE;
>> ?}
>>
>> ?static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
>> @@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>> ? ? ? ? ? ? ? struct nand_chip *chip, uint8_t *buf, int page)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>>
>> ? ? ? chip->read_buf(mtd, buf, mtd->writesize);
>> ? ? ? chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>>
>> - ? ? if (info->retcode == ERR_SBERR) {
>> + ? ? if (nand->retcode == ERR_SBERR) {
>> ? ? ? ? ? ? ? switch (info->use_ecc) {
>> ? ? ? ? ? ? ? case 1:
>> ? ? ? ? ? ? ? ? ? ? ? mtd->ecc_stats.corrected++;
>> @@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>> ? ? ? ? ? ? ? default:
>> ? ? ? ? ? ? ? ? ? ? ? break;
>> ? ? ? ? ? ? ? }
>> - ? ? } else if (info->retcode == ERR_DBERR) {
>> + ? ? } else if (nand->retcode == ERR_DBERR) {
>> ? ? ? ? ? ? ? /*
>> ? ? ? ? ? ? ? ?* for blank page (all 0xff), HW will calculate its ECC as
>> ? ? ? ? ? ? ? ?* 0, which is different from the ECC information within
>> ? ? ? ? ? ? ? ?* OOB, ignore such double bit errors
>> ? ? ? ? ? ? ? ?*/
>> ? ? ? ? ? ? ? if (is_buf_blank(buf, mtd->writesize))
>> - ? ? ? ? ? ? ? ? ? ? info->retcode = ERR_NONE;
>> + ? ? ? ? ? ? ? ? ? ? nand->retcode = ERR_NONE;
>> ? ? ? ? ? ? ? else
>> ? ? ? ? ? ? ? ? ? ? ? mtd->ecc_stats.failed++;
>> ? ? ? }
>> @@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>> ?static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> ? ? ? char retval = 0xFF;
>>
>> - ? ? if (info->buf_start < info->buf_count)
>> + ? ? if (nand->buf_start < nand->buf_count)
>> ? ? ? ? ? ? ? /* Has just send a new command? */
>> - ? ? ? ? ? ? retval = info->data_buff[info->buf_start++];
>> + ? ? ? ? ? ? retval = nand->data_buff[nand->buf_start++];
>>
>> ? ? ? return retval;
>> ?}
>> @@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>> ?static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> ? ? ? u16 retval = 0xFFFF;
>>
>> - ? ? if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
>> - ? ? ? ? ? ? retval = *((u16 *)(info->data_buff+info->buf_start));
>> - ? ? ? ? ? ? info->buf_start += 2;
>> + ? ? if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
>> + ? ? ? ? ? ? retval = *((u16 *)(nand->data_buff+nand->buf_start));
>> + ? ? ? ? ? ? nand->buf_start += 2;
>> ? ? ? }
>> ? ? ? return retval;
>> ?}
>> @@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>> ?static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> - ? ? int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>>
>> - ? ? memcpy(buf, info->data_buff + info->buf_start, real_len);
>> - ? ? info->buf_start += real_len;
>> + ? ? memcpy(buf, nand->data_buff + nand->buf_start, real_len);
>> + ? ? nand->buf_start += real_len;
>> ?}
>>
>> ?static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
>> ? ? ? ? ? ? ? const uint8_t *buf, int len)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> - ? ? int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>>
>> - ? ? memcpy(info->data_buff + info->buf_start, buf, real_len);
>> - ? ? info->buf_start += real_len;
>> + ? ? memcpy(nand->data_buff + nand->buf_start, buf, real_len);
>> + ? ? nand->buf_start += real_len;
>> ?}
>>
>> ?static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
>> @@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>> ?static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>>
>> ? ? ? /* pxa3xx_nand_send_command has waited for command complete */
>> ? ? ? if (this->state == FL_WRITING || this->state == FL_ERASING) {
>> - ? ? ? ? ? ? if (info->retcode == ERR_NONE)
>> + ? ? ? ? ? ? if (nand->retcode == ERR_NONE)
>> ? ? ? ? ? ? ? ? ? ? ? return 0;
>> ? ? ? ? ? ? ? else {
>> ? ? ? ? ? ? ? ? ? ? ? /*
>> @@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>> ?static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const struct pxa3xx_nand_flash *f)
>> ?{
>> - ? ? struct platform_device *pdev = info->pdev;
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? struct platform_device *pdev = nand->pdev;
>> ? ? ? struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>> ? ? ? uint32_t ndcr = 0x0; /* enable all interrupts */
>>
>> @@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>> ? ? ? ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>>
>> ? ? ? info->reg_ndcr = ndcr;
>> + ? ? info->use_ecc = 1;
>>
>> ? ? ? pxa3xx_nand_set_timing(info, f->timing);
>> ? ? ? return 0;
>> @@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>>
>> ?static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>> ?{
>> - ? ? uint32_t ndcr = nand_readl(info, NDCR);
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? uint32_t ndcr = nand_readl(nand, NDCR);
>> +
>> + ? ? if (info->chip_select > 0) {
>> + ? ? ? ? ? ? printk(KERN_ERR "We could not detect configure"
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? " if more than one cs is supported!!\n");
>> + ? ? ? ? ? ? BUG();
>
> like Daniel already noticed, may be dev_err() is enough?
Would change to dev_err();

>
>> + ? ? }
>> ? ? ? info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
>> ? ? ? /* set info fields needed to read id */
>> ? ? ? info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
>> ? ? ? info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
>> ? ? ? info->cmdset = &default_cmdset;
>>
>> - ? ? info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
>> - ? ? info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
>> + ? ? info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
>> + ? ? info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
>>
>> ? ? ? return 0;
>> ?}
>> @@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>> ? */
>> ?#define MAX_BUFF_SIZE ? ? ? ?PAGE_SIZE
>>
>> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
>> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
>> ?{
>> - ? ? struct platform_device *pdev = info->pdev;
>> - ? ? int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>> -
>> - ? ? if (use_dma == 0) {
>> - ? ? ? ? ? ? info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
>> - ? ? ? ? ? ? if (info->data_buff == NULL)
>> - ? ? ? ? ? ? ? ? ? ? return -ENOMEM;
>> - ? ? ? ? ? ? return 0;
>> - ? ? }
>> -
>> - ? ? info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? &info->data_buff_phys, GFP_KERNEL);
>> - ? ? if (info->data_buff == NULL) {
>> - ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate dma buffer\n");
>> - ? ? ? ? ? ? return -ENOMEM;
>> - ? ? }
>> -
>> - ? ? info->data_buff_size = MAX_BUFF_SIZE;
>> - ? ? info->data_desc = (void *)info->data_buff + data_desc_offset;
>> - ? ? info->data_desc_addr = info->data_buff_phys + data_desc_offset;
>> + ? ? struct pxa3xx_nand *nand;
>> + ? ? struct mtd_info *mtd;
>>
>> - ? ? info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_data_dma_irq, info);
>> - ? ? if (info->data_dma_ch < 0) {
>> - ? ? ? ? ? ? dev_err(&pdev->dev, "failed to request data dma\n");
>> - ? ? ? ? ? ? dma_free_coherent(&pdev->dev, info->data_buff_size,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->data_buff, info->data_buff_phys);
>> - ? ? ? ? ? ? return info->data_dma_ch;
>> - ? ? }
>> + ? ? if (!info)
>> + ? ? ? ? ? ? return;
>>
>> - ? ? return 0;
>> + ? ? nand = info->nand_data;
>> + ? ? mtd = get_mtd_by_info(info);
>> + ? ? kfree(mtd);
>> + ? ? nand->info[cs] = NULL;
>> ?}
>>
>> ?static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>> ?{
>> - ? ? struct mtd_info *mtd = info->mtd;
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? struct mtd_info *mtd = get_mtd_by_info(info);
>> ? ? ? struct nand_chip *chip = mtd->priv;
>>
>> ? ? ? /* use the common timing to make a try */
>> - ? ? pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
>> + ? ? if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
>> + ? ? ? ? ? ? return 0;
>> ? ? ? chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
>> - ? ? if (info->is_ready)
>> + ? ? if (nand->is_ready)
>> ? ? ? ? ? ? ? return 1;
>> ? ? ? else
>> ? ? ? ? ? ? ? return 0;
>
> I think it is time to change this function return convention to propagate
> errors and not just 0 or 1, (may be in separate patch) what do you think?

How about return 0 when sensing the READY signal, or return -ENODEV?

>
>> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>> ?static int pxa3xx_nand_scan(struct mtd_info *mtd)
>> ?{
>> ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> - ? ? struct platform_device *pdev = info->pdev;
>> + ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? struct platform_device *pdev = nand->pdev;
>> ? ? ? struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>> ? ? ? struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>> ? ? ? const struct pxa3xx_nand_flash *f = NULL;
>> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>> ? ? ? uint64_t chipsize;
>> ? ? ? int i, ret, num;
>>
>> + ? ? nand->chip_select = info->chip_select;
>> ? ? ? if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>> ? ? ? ? ? ? ? goto KEEP_CONFIG;
>>
>> ? ? ? ret = pxa3xx_nand_sensing(info);
>> ? ? ? if (!ret) {
>> - ? ? ? ? ? ? kfree(mtd);
>> - ? ? ? ? ? ? info->mtd = NULL;
>> - ? ? ? ? ? ? printk(KERN_INFO "There is no nand chip on cs 0!\n");
>> + ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
>> + ? ? ? ? ? ? printk(KERN_INFO "There is no nand chip on cs %d!\n",
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->chip_select);
>>
>> ? ? ? ? ? ? ? return -EINVAL;
>> ? ? ? }
>>
>> ? ? ? chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>> - ? ? id = *((uint16_t *)(info->data_buff));
>> + ? ? id = *((uint16_t *)(nand->data_buff));
>> ? ? ? if (id != 0)
>> ? ? ? ? ? ? ? printk(KERN_INFO "Detect a flash id %x\n", id);
>> ? ? ? else {
>> - ? ? ? ? ? ? kfree(mtd);
>> - ? ? ? ? ? ? info->mtd = NULL;
>> - ? ? ? ? ? ? printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
>
> Is this warning no longer needed?
Would readd it in the next post.

>
>> -
>> + ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
>> ? ? ? ? ? ? ? return -EINVAL;
>> ? ? ? }
>>
>> @@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>> ? ? ? }
>>
>> ? ? ? if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
>> - ? ? ? ? ? ? kfree(mtd);
>> - ? ? ? ? ? ? info->mtd = NULL;
>> + ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
>> ? ? ? ? ? ? ? printk(KERN_ERR "ERROR!! flash not defined!!!\n");
>>
>> ? ? ? ? ? ? ? return -EINVAL;
>> ? ? ? }
>>
>> - ? ? pxa3xx_nand_config_flash(info, f);
>> + ? ? if (pxa3xx_nand_config_flash(info, f)) {
>> + ? ? ? ? ? ? printk(KERN_ERR "ERROR! Configure failed\n");
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>
> Although, the pxa3xx_nand_config_flash() returns only 0 or -EINVAL,
> it is better to propagate its return value.

Ok, would apply this.

>
>> ? ? ? pxa3xx_flash_ids[0].name = f->name;
>> ? ? ? pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
>> ? ? ? pxa3xx_flash_ids[0].pagesize = f->page_size;
>> @@ -950,13 +977,13 @@ KEEP_CONFIG:
>> ? ? ? if (nand_scan_ident(mtd, 1, def))
>> ? ? ? ? ? ? ? return -ENODEV;
>> ? ? ? /* calculate addressing information */
>> + ? ? nand->oob_buff = nand->data_buff + mtd->writesize;
>> ? ? ? info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
>> - ? ? info->oob_buff = info->data_buff + mtd->writesize;
>> ? ? ? if ((mtd->size >> chip->page_shift) > 65536)
>> ? ? ? ? ? ? ? info->row_addr_cycles = 3;
>> ? ? ? else
>> ? ? ? ? ? ? ? info->row_addr_cycles = 2;
>> - ? ? mtd->name = mtd_names[0];
>> + ? ? mtd->name = mtd_names[nand->chip_select];
>> ? ? ? chip->ecc.mode = NAND_ECC_HW;
>> ? ? ? chip->ecc.size = info->page_size;
>>
>> @@ -967,51 +994,33 @@ KEEP_CONFIG:
>> ? ? ? return nand_scan_tail(mtd);
>> ?}
>>
>> -static
>> -struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>> +static int alloc_nand_resource(struct platform_device *pdev)
>> ?{
>> + ? ? struct pxa3xx_nand_platform_data *pdata;
>> ? ? ? struct pxa3xx_nand_info *info;
>> ? ? ? struct nand_chip *chip;
>> ? ? ? struct mtd_info *mtd;
>> + ? ? struct pxa3xx_nand *nand;
>> ? ? ? struct resource *r;
>> - ? ? int ret, irq;
>> + ? ? int ret, irq, cs;
>> + ? ? int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>>
>> - ? ? mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
>> - ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
>> - ? ? if (!mtd) {
>> + ? ? pdata = pdev->dev.platform_data;
>> + ? ? nand = kzalloc(sizeof(struct mtd_info)
>> + ? ? ? ? ? ? ? ? ? ? + sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
>> + ? ? if (!nand) {
>> ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate memory\n");
>> - ? ? ? ? ? ? return NULL;
>> + ? ? ? ? ? ? return -ENOMEM;
>> ? ? ? }
>>
>> - ? ? info = (struct pxa3xx_nand_info *)(&mtd[1]);
>> - ? ? chip = (struct nand_chip *)(&mtd[1]);
>> - ? ? info->pdev = pdev;
>> - ? ? info->mtd = mtd;
>> - ? ? mtd->priv = info;
>> - ? ? mtd->owner = THIS_MODULE;
>> -
>> - ? ? chip->ecc.read_page ? ? = pxa3xx_nand_read_page_hwecc;
>> - ? ? chip->ecc.write_page ? ?= pxa3xx_nand_write_page_hwecc;
>> - ? ? chip->controller ? ? ? ?= &info->controller;
>> - ? ? chip->waitfunc ? ? ? ? ?= pxa3xx_nand_waitfunc;
>> - ? ? chip->select_chip ? ? ? = pxa3xx_nand_select_chip;
>> - ? ? chip->dev_ready ? ? ? ? = pxa3xx_nand_dev_ready;
>> - ? ? chip->cmdfunc ? ? ? ? ? = pxa3xx_nand_cmdfunc;
>> - ? ? chip->read_word ? ? ? ? = pxa3xx_nand_read_word;
>> - ? ? chip->read_byte ? ? ? ? = pxa3xx_nand_read_byte;
>> - ? ? chip->read_buf ? ? ? ? ?= pxa3xx_nand_read_buf;
>> - ? ? chip->write_buf ? ? ? ? = pxa3xx_nand_write_buf;
>> - ? ? chip->verify_buf ? ? ? ?= pxa3xx_nand_verify_buf;
>> -
>> - ? ? spin_lock_init(&chip->controller->lock);
>> - ? ? init_waitqueue_head(&chip->controller->wq);
>> - ? ? info->clk = clk_get(&pdev->dev, NULL);
>> - ? ? if (IS_ERR(info->clk)) {
>> + ? ? nand->pdev = pdev;
>> + ? ? nand->clk = clk_get(&pdev->dev, NULL);
>> + ? ? if (IS_ERR(nand->clk)) {
>> ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to get nand clock\n");
>> - ? ? ? ? ? ? ret = PTR_ERR(info->clk);
>> - ? ? ? ? ? ? goto fail_free_mtd;
>> + ? ? ? ? ? ? ret = PTR_ERR(nand->clk);
>> + ? ? ? ? ? ? goto fail_alloc;
>> ? ? ? }
>> - ? ? clk_enable(info->clk);
>> + ? ? clk_enable(nand->clk);
>>
>> ? ? ? r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>> ? ? ? if (r == NULL) {
>> @@ -1019,7 +1028,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>> ? ? ? ? ? ? ? ret = -ENXIO;
>> ? ? ? ? ? ? ? goto fail_put_clk;
>> ? ? ? }
>> - ? ? info->drcmr_dat = r->start;
>> + ? ? nand->drcmr_dat = r->start;
>>
>> ? ? ? r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
>> ? ? ? if (r == NULL) {
>> @@ -1027,7 +1036,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>> ? ? ? ? ? ? ? ret = -ENXIO;
>> ? ? ? ? ? ? ? goto fail_put_clk;
>> ? ? ? }
>> - ? ? info->drcmr_cmd = r->start;
>> + ? ? nand->drcmr_cmd = r->start;
>>
>> ? ? ? irq = platform_get_irq(pdev, 0);
>> ? ? ? if (irq < 0) {
>> @@ -1050,81 +1059,147 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>> ? ? ? ? ? ? ? goto fail_put_clk;
>> ? ? ? }
>>
>> - ? ? info->mmio_base = ioremap(r->start, resource_size(r));
>> - ? ? if (info->mmio_base == NULL) {
>> + ? ? nand->mmio_base = ioremap(r->start, resource_size(r));
>> + ? ? if (nand->mmio_base == NULL) {
>> ? ? ? ? ? ? ? dev_err(&pdev->dev, "ioremap() failed\n");
>> ? ? ? ? ? ? ? ret = -ENODEV;
>> ? ? ? ? ? ? ? goto fail_free_res;
>> ? ? ? }
>> - ? ? info->mmio_phys = r->start;
>> -
>> - ? ? ret = pxa3xx_nand_init_buff(info);
>> - ? ? if (ret)
>> - ? ? ? ? ? ? goto fail_free_io;
>> + ? ? nand->mmio_phys = r->start;
>>
>> ? ? ? /* initialize all interrupts to be disabled */
>> - ? ? disable_int(info, NDSR_MASK);
>> + ? ? disable_int(nand, NDSR_MASK);
>>
>> ? ? ? ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED,
>> - ? ? ? ? ? ? ? ? ? ? ? pdev->name, info);
>> + ? ? ? ? ? ? ? ? ? ? ? pdev->name, nand);
>> ? ? ? if (ret < 0) {
>> ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to request IRQ\n");
>> - ? ? ? ? ? ? goto fail_free_buf;
>> + ? ? ? ? ? ? ret = -ENXIO;
>> + ? ? ? ? ? ? goto fail_free_io;
>> + ? ? }
>> +
>> + ? ? platform_set_drvdata(pdev, nand);
>> +
>> + ? ? spin_lock_init(&nand->controller.lock);
>> + ? ? init_waitqueue_head(&nand->controller.wq);
>> + ? ? for (cs = 0; cs < pdata->cs_num; cs++) {
>> + ? ? ? ? ? ? mtd = kzalloc(sizeof(struct mtd_info)
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? + sizeof(struct pxa3xx_nand_info),
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
>> + ? ? ? ? ? ? if (!mtd) {
>> + ? ? ? ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate memory\n");
>> + ? ? ? ? ? ? ? ? ? ? ret = -ENOMEM;
>> + ? ? ? ? ? ? ? ? ? ? goto fail_free_irq;
>> + ? ? ? ? ? ? }
>> +
>> + ? ? ? ? ? ? info = (struct pxa3xx_nand_info *)(&mtd[1]);
>> + ? ? ? ? ? ? info->nand_data = nand;
>> + ? ? ? ? ? ? info->chip_select = cs;
>> + ? ? ? ? ? ? mtd->priv = info;
>> + ? ? ? ? ? ? mtd->owner = THIS_MODULE;
>> + ? ? ? ? ? ? nand->info[cs] = info;
>> +
>> + ? ? ? ? ? ? chip = (struct nand_chip *)(&mtd[1]);
>> + ? ? ? ? ? ? chip->controller ? ? ? ?= &nand->controller;
>> + ? ? ? ? ? ? chip->ecc.read_page ? ? = pxa3xx_nand_read_page_hwecc;
>> + ? ? ? ? ? ? chip->ecc.write_page ? ?= pxa3xx_nand_write_page_hwecc;
>> + ? ? ? ? ? ? chip->waitfunc ? ? ? ? ?= pxa3xx_nand_waitfunc;
>> + ? ? ? ? ? ? chip->select_chip ? ? ? = pxa3xx_nand_select_chip;
>> + ? ? ? ? ? ? chip->cmdfunc ? ? ? ? ? = pxa3xx_nand_cmdfunc;
>> + ? ? ? ? ? ? chip->read_word ? ? ? ? = pxa3xx_nand_read_word;
>> + ? ? ? ? ? ? chip->read_byte ? ? ? ? = pxa3xx_nand_read_byte;
>> + ? ? ? ? ? ? chip->read_buf ? ? ? ? ?= pxa3xx_nand_read_buf;
>> + ? ? ? ? ? ? chip->write_buf ? ? ? ? = pxa3xx_nand_write_buf;
>> + ? ? ? ? ? ? chip->verify_buf ? ? ? ?= pxa3xx_nand_verify_buf;
>> ? ? ? }
>>
>> - ? ? platform_set_drvdata(pdev, info);
>> + ? ? if (use_dma == 0) {
>> + ? ? ? ? ? ? nand->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
>> + ? ? ? ? ? ? if (nand->data_buff == NULL) {
>> + ? ? ? ? ? ? ? ? ? ? ret = -ENOMEM;
>> + ? ? ? ? ? ? ? ? ? ? goto fail_free_buf;
>> + ? ? ? ? ? ? }
>> + ? ? ? ? ? ? goto success_exit;
>> + ? ? }
>>
>> - ? ? return info;
>> + ? ? nand->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
>> + ? ? ? ? ? ? ? ? ? ? &nand->data_buff_phys, GFP_KERNEL);
>> + ? ? if (nand->data_buff == NULL) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate dma buffer\n");
>> + ? ? ? ? ? ? ret = -ENOMEM;
>> + ? ? ? ? ? ? goto fail_free_buf;
>> + ? ? }
>>
>> + ? ? nand->data_desc = (void *)nand->data_buff + data_desc_offset;
>> + ? ? nand->data_desc_addr = nand->data_buff_phys + data_desc_offset;
>> + ? ? nand->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
>> + ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_data_dma_irq, nand);
>> + ? ? if (nand->data_dma_ch < 0) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "failed to request data dma\n");
>> + ? ? ? ? ? ? ret = -ENXIO;
>> + ? ? ? ? ? ? goto fail_free_dma_buf;
>> + ? ? }
>> +success_exit:
>> + ? ? return 0;
>> +
>> +fail_free_dma_buf:
>> + ? ? dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
>> + ? ? ? ? ? ? ? ? ? ? nand->data_buff, nand->data_buff_phys);
>> ?fail_free_buf:
>> - ? ? free_irq(irq, info);
>> - ? ? if (use_dma) {
>> - ? ? ? ? ? ? pxa_free_dma(info->data_dma_ch);
>> - ? ? ? ? ? ? dma_free_coherent(&pdev->dev, info->data_buff_size,
>> - ? ? ? ? ? ? ? ? ? ? info->data_buff, info->data_buff_phys);
>> - ? ? } else
>> - ? ? ? ? ? ? kfree(info->data_buff);
>> + ? ? for (cs = 0; cs < pdata->cs_num; cs++) {
>> + ? ? ? ? ? ? info = nand->info[cs];
>> + ? ? ? ? ? ? free_cs_resource(info, cs);
>> + ? ? }
>> +fail_free_irq:
>> + ? ? free_irq(irq, nand);
>> ?fail_free_io:
>> - ? ? iounmap(info->mmio_base);
>> + ? ? iounmap(nand->mmio_base);
>> ?fail_free_res:
>> ? ? ? release_mem_region(r->start, resource_size(r));
>> ?fail_put_clk:
>> - ? ? clk_disable(info->clk);
>> - ? ? clk_put(info->clk);
>> -fail_free_mtd:
>> - ? ? kfree(mtd);
>> - ? ? return NULL;
>> + ? ? clk_disable(nand->clk);
>> + ? ? clk_put(nand->clk);
>> +fail_alloc:
>> + ? ? kfree(nand);
>> + ? ? return ret;
>> ?}
>>
>> ?static int pxa3xx_nand_remove(struct platform_device *pdev)
>> ?{
>> - ? ? struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>> - ? ? struct mtd_info *mtd = info->mtd;
>> + ? ? struct pxa3xx_nand *nand = platform_get_drvdata(pdev);
>> + ? ? struct pxa3xx_nand_platform_data *pdata;
>> + ? ? struct pxa3xx_nand_info *info;
>> + ? ? struct mtd_info *mtd;
>> ? ? ? struct resource *r;
>> - ? ? int irq;
>> + ? ? int irq, cs;
>>
>> ? ? ? platform_set_drvdata(pdev, NULL);
>> + ? ? pdata = pdev->dev.platform_data;
>>
>> ? ? ? irq = platform_get_irq(pdev, 0);
>> ? ? ? if (irq >= 0)
>> - ? ? ? ? ? ? free_irq(irq, info);
>> + ? ? ? ? ? ? free_irq(irq, nand);
>> ? ? ? if (use_dma) {
>> - ? ? ? ? ? ? pxa_free_dma(info->data_dma_ch);
>> - ? ? ? ? ? ? dma_free_writecombine(&pdev->dev, info->data_buff_size,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->data_buff, info->data_buff_phys);
>> + ? ? ? ? ? ? pxa_free_dma(nand->data_dma_ch);
>> + ? ? ? ? ? ? dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->data_buff, nand->data_buff_phys);
>> ? ? ? } else
>> - ? ? ? ? ? ? kfree(info->data_buff);
>> + ? ? ? ? ? ? kfree(nand->data_buff);
>>
>> - ? ? iounmap(info->mmio_base);
>> + ? ? iounmap(nand->mmio_base);
>> ? ? ? r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> ? ? ? release_mem_region(r->start, resource_size(r));
>>
>> - ? ? clk_disable(info->clk);
>> - ? ? clk_put(info->clk);
>> + ? ? clk_disable(nand->clk);
>> + ? ? clk_put(nand->clk);
>>
>> - ? ? if (mtd) {
>> + ? ? for (cs = 0; cs < pdata->cs_num; cs++) {
>> + ? ? ? ? ? ? info = nand->info[cs];
>> + ? ? ? ? ? ? if (!info)
>> + ? ? ? ? ? ? ? ? ? ? continue;
>> + ? ? ? ? ? ? mtd = get_mtd_by_info(info);
>> ? ? ? ? ? ? ? mtd_device_unregister(mtd);
>> - ? ? ? ? ? ? kfree(mtd);
>> + ? ? ? ? ? ? free_cs_resource(info, cs);
>> ? ? ? }
>> ? ? ? return 0;
>> ?}
>> @@ -1134,34 +1209,54 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
>> ? ? ? struct pxa3xx_nand_platform_data *pdata;
>> ? ? ? struct pxa3xx_nand_info *info;
>>
>> + ? ? struct pxa3xx_nand *nand;
>> + ? ? struct mtd_info *mtd;
>> + ? ? int cs, ret, nr_parts, probe_success;
>> +
>> + ? ? probe_success = 0;
>
> Can this be done along with declaration?
Ok, would apply to the next post.

>
>> ? ? ? pdata = pdev->dev.platform_data;
>> ? ? ? if (!pdata) {
>> ? ? ? ? ? ? ? dev_err(&pdev->dev, "no platform data defined\n");
>> ? ? ? ? ? ? ? return -ENODEV;
>> ? ? ? }
>>
>> - ? ? info = alloc_nand_resource(pdev);
>> - ? ? if (info == NULL)
>> + ? ? ret = alloc_nand_resource(pdev);
>> + ? ? if (ret)
>> ? ? ? ? ? ? ? return -ENOMEM;
>
> Why not propagate the return value of alloc_nand_resource()?

Would apply propagate error to the next post.

>
>>
>> - ? ? if (pxa3xx_nand_scan(info->mtd)) {
>> - ? ? ? ? ? ? dev_err(&pdev->dev, "failed to scan nand\n");
>> - ? ? ? ? ? ? pxa3xx_nand_remove(pdev);
>> - ? ? ? ? ? ? return -ENODEV;
>> - ? ? }
>> + ? ? nand = platform_get_drvdata(pdev);
>> + ? ? for (cs = 0; cs < pdata->cs_num; cs++) {
>> + ? ? ? ? ? ? info = nand->info[cs];
>> + ? ? ? ? ? ? mtd = get_mtd_by_info(info);
>> + ? ? ? ? ? ? if (pxa3xx_nand_scan(mtd)) {
>> + ? ? ? ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to scan nand\n");
>
> I think, it would be useful also to print here the return value of pxa3xx_nand_scan().
Ok, would apply to the next post

>
>> + ? ? ? ? ? ? ? ? ? ? continue;
>> + ? ? ? ? ? ? }
>> +
>> + ? ? ? ? ? ? ret = 0;
>> + ? ? ? ? ? ? nr_parts = 0;
>> + ? ? ? ? ? ? if (mtd_has_cmdlinepart()) {
>> + ? ? ? ? ? ? ? ? ? ? const char *probes[] = { "cmdlinepart", NULL };
>> + ? ? ? ? ? ? ? ? ? ? struct mtd_partition *parts;
>>
>> - ? ? if (mtd_has_cmdlinepart()) {
>> - ? ? ? ? ? ? const char *probes[] = { "cmdlinepart", NULL };
>> - ? ? ? ? ? ? struct mtd_partition *parts;
>> - ? ? ? ? ? ? int nr_parts;
>> + ? ? ? ? ? ? ? ? ? ? nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0);
>>
>> - ? ? ? ? ? ? nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0);
>> + ? ? ? ? ? ? ? ? ? ? if (nr_parts)
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = mtd_device_register(mtd, parts, nr_parts);
>> + ? ? ? ? ? ? }
>>
>> - ? ? ? ? ? ? if (nr_parts)
>> - ? ? ? ? ? ? ? ? ? ? return mtd_device_register(info->mtd, parts, nr_parts);
>> + ? ? ? ? ? ? if (!nr_parts)
>> + ? ? ? ? ? ? ? ? ? ? ret = mtd_device_register(mtd, pdata->parts[cs],
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pdata->nr_parts[cs]);
>> + ? ? ? ? ? ? if (!ret)
>> + ? ? ? ? ? ? ? ? ? ? probe_success = 1;
>> ? ? ? }
>>
>> - ? ? return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
>> + ? ? if (!probe_success) {
>> + ? ? ? ? ? ? pxa3xx_nand_remove(pdev);
>> + ? ? ? ? ? ? return -ENODEV;
>> + ? ? } else
>
> You don't need this else statement

Would apply to the next post.

>
>> + ? ? ? ? ? ? return 0;
>> ?}
>>
>> ?#ifdef CONFIG_PM
>> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>> ? ? ? struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>> ? ? ? struct mtd_info *mtd = info->mtd;
>>
>> - ? ? nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>> - ? ? nand_writel(info, NDTR1CS0, info->ndtr1cs0);
>> + ? ? nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>> + ? ? nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>> ? ? ? clk_enable(info->clk);
>>
>> ? ? ? return 0;
>
> I won't be able to test the patch in the near future, sorry...
>

Thanks for comments.

Best regards,
Lei

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-08 16:00 ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08 16:00 UTC (permalink / raw)
  To: linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
 9 files changed, 444 insertions(+), 338 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..b6589d6 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.cs_num = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..d67eb7b 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..ff7a07b 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..6eaf852 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..e7ce135 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.cs_num		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..a54846d 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..ea4752a 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.cs_num		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..34a3f52 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,8 @@ struct pxa3xx_nand_flash {
 	struct pxa3xx_nand_timing *timing;	/* NAND Flash timing */
 };
 
+/* 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
@@ -52,8 +54,10 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip select would be used for this platform */
+	int	cs_num;
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 30689cc..259b8d5 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -92,11 +92,13 @@
 #define NDCB0_ADDR_CYC_SHIFT	(16)
 
 /* macros for registers read/write */
-#define nand_writel(info, off, val)	\
-	__raw_writel((val), (info)->mmio_base + (off))
+#define nand_writel(nand, off, val)	\
+	__raw_writel((val), (nand)->mmio_base + (off))
 
-#define nand_readl(info, off)		\
-	__raw_readl((info)->mmio_base + (off))
+#define nand_readl(nand, off)		\
+	__raw_readl((nand)->mmio_base + (off))
+#define get_mtd_by_info(info)		\
+	(struct mtd_info *)((void *)info - sizeof(struct mtd_info))
 
 /* error code and state */
 enum {
@@ -110,6 +112,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -123,63 +126,63 @@ enum {
 struct pxa3xx_nand_info {
 	struct nand_chip	nand_chip;
 
-	struct nand_hw_control	controller;
-	struct platform_device	 *pdev;
 	struct pxa3xx_nand_cmdset *cmdset;
+	/* page size of attached chip */
+	uint16_t		page_size;
+	uint8_t			chip_select;
+	uint8_t			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	uint8_t			col_addr_cycles;
+	uint8_t			row_addr_cycles;
+	uint8_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
 
+	void			*nand_data;
+};
+
+struct pxa3xx_nand {
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct nand_hw_control	controller;
+	struct completion	cmd_complete;
+	struct platform_device	 *pdev;
 
-	unsigned int 		buf_start;
-	unsigned int		buf_count;
-
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
-
-	unsigned char		*data_buff;
-	unsigned char		*oob_buff;
-	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
-	struct pxa_dma_desc	*data_desc;
+	dma_addr_t		data_buff_phys;
 	dma_addr_t 		data_desc_addr;
+	struct pxa_dma_desc	*data_desc;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
+	struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
+	uint32_t		command;
+	uint16_t		data_size;	/* data size in FIFO */
+	uint16_t		oob_size;
+	unsigned char		*data_buff;
+	unsigned char		*oob_buff;
+	uint32_t		buf_start;
+	uint32_t		buf_count;
 
 	/* relate to the command */
 	unsigned int		state;
-
+	uint8_t			chip_select;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
-
-	unsigned int		page_size;	/* page size of attached chip */
-	unsigned int		data_size;	/* data size in FIFO */
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
+	uint8_t			total_cmds;
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 				   const struct pxa3xx_nand_timing *t)
 {
-	unsigned long nand_clk = clk_get_rate(info->clk);
+	struct pxa3xx_nand *nand = info->nand_data;
+	unsigned long nand_clk;
 	uint32_t ndtr0, ndtr1;
 
+	nand_clk = clk_get_rate(nand->clk);
 	ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
 		NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
 		NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
@@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 
 	info->ndtr0cs0 = ndtr0;
 	info->ndtr1cs0 = ndtr1;
-	nand_writel(info, NDTR0CS0, ndtr0);
-	nand_writel(info, NDTR1CS0, ndtr1);
+	nand_writel(nand, NDTR0CS0, ndtr0);
+	nand_writel(nand, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand *nand = info->nand_data;
 	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	nand->data_size = info->page_size;
 	if (!oob_enable) {
-		info->oob_size = 0;
+		nand->oob_size = 0;
 		return;
 	}
 
 	switch (info->page_size) {
 	case 2048:
-		info->oob_size = (info->use_ecc) ? 40 : 64;
+		nand->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
 	case 512:
-		info->oob_size = (info->use_ecc) ? 8 : 16;
+		nand->oob_size = (info->use_ecc) ? 8 : 16;
 		break;
 	}
 }
@@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  * 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_info *info)
+static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
 {
+	struct pxa3xx_nand_info *info;
 	uint32_t ndcr;
 
+	info = nand->info[nand->chip_select];
 	ndcr = info->reg_ndcr;
-	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
-	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
+	ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
+	ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
 
 	/* clear status bits and run */
-	nand_writel(info, NDCR, 0);
-	nand_writel(info, NDSR, NDSR_MASK);
-	nand_writel(info, NDCR, ndcr);
+	nand_writel(nand, NDCR, 0);
+	nand_writel(nand, NDSR, NDSR_MASK);
+	nand_writel(nand, NDCR, ndcr);
 }
 
-static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
+static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
 {
 	uint32_t ndcr;
 	int timeout = NAND_STOP_DELAY;
 
 	/* wait RUN bit in NDCR become 0 */
-	ndcr = nand_readl(info, NDCR);
+	ndcr = nand_readl(nand, NDCR);
 	while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
-		ndcr = nand_readl(info, NDCR);
+		ndcr = nand_readl(nand, NDCR);
 		udelay(1);
 	}
 
 	if (timeout <= 0) {
 		ndcr &= ~NDCR_ND_RUN;
-		nand_writel(info, NDCR, ndcr);
+		nand_writel(nand, NDCR, ndcr);
 	}
 	/* clear status bits */
-	nand_writel(info, NDSR, NDSR_MASK);
+	nand_writel(nand, NDSR, NDSR_MASK);
 }
 
-static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
+static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
 {
 	uint32_t ndcr;
 
-	ndcr = nand_readl(info, NDCR);
-	nand_writel(info, NDCR, ndcr & ~int_mask);
+	ndcr = nand_readl(nand, NDCR);
+	nand_writel(nand, NDCR, ndcr & ~int_mask);
 }
 
-static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
+static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
 {
 	uint32_t ndcr;
 
-	ndcr = nand_readl(info, NDCR);
-	nand_writel(info, NDCR, ndcr | int_mask);
+	ndcr = nand_readl(nand, NDCR);
+	nand_writel(nand, NDCR, ndcr | int_mask);
 }
 
-static void handle_data_pio(struct pxa3xx_nand_info *info)
+static void handle_data_pio(struct pxa3xx_nand *nand)
 {
-	switch (info->state) {
+	switch (nand->state) {
 	case STATE_PIO_WRITING:
-		__raw_writesl(info->mmio_base + NDDB, info->data_buff,
-				DIV_ROUND_UP(info->data_size, 4));
-		if (info->oob_size > 0)
-			__raw_writesl(info->mmio_base + NDDB, info->oob_buff,
-					DIV_ROUND_UP(info->oob_size, 4));
+		__raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
+				DIV_ROUND_UP(nand->data_size, 4));
+		if (nand->oob_size > 0)
+			__raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
+					DIV_ROUND_UP(nand->oob_size, 4));
 		break;
 	case STATE_PIO_READING:
-		__raw_readsl(info->mmio_base + NDDB, info->data_buff,
-				DIV_ROUND_UP(info->data_size, 4));
-		if (info->oob_size > 0)
-			__raw_readsl(info->mmio_base + NDDB, info->oob_buff,
-					DIV_ROUND_UP(info->oob_size, 4));
+		__raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
+				DIV_ROUND_UP(nand->data_size, 4));
+		if (nand->oob_size > 0)
+			__raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
+					DIV_ROUND_UP(nand->oob_size, 4));
 		break;
 	default:
 		printk(KERN_ERR "%s: invalid state %d\n", __func__,
-				info->state);
+				nand->state);
 		BUG();
 	}
 }
 
-static void start_data_dma(struct pxa3xx_nand_info *info)
+static void start_data_dma(struct pxa3xx_nand *nand)
 {
-	struct pxa_dma_desc *desc = info->data_desc;
-	int dma_len = ALIGN(info->data_size + info->oob_size, 32);
+	struct pxa_dma_desc *desc = nand->data_desc;
+	int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
 
 	desc->ddadr = DDADR_STOP;
 	desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
 
-	switch (info->state) {
+	switch (nand->state) {
 	case STATE_DMA_WRITING:
-		desc->dsadr = info->data_buff_phys;
-		desc->dtadr = info->mmio_phys + NDDB;
+		desc->dsadr = nand->data_buff_phys;
+		desc->dtadr = nand->mmio_phys + NDDB;
 		desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
 		break;
 	case STATE_DMA_READING:
-		desc->dtadr = info->data_buff_phys;
-		desc->dsadr = info->mmio_phys + NDDB;
+		desc->dtadr = nand->data_buff_phys;
+		desc->dsadr = nand->mmio_phys + NDDB;
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
 		printk(KERN_ERR "%s: invalid state %d\n", __func__,
-				info->state);
+				nand->state);
 		BUG();
 	}
 
-	DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
-	DDADR(info->data_dma_ch) = info->data_desc_addr;
-	DCSR(info->data_dma_ch) |= DCSR_RUN;
+	DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
+	DDADR(nand->data_dma_ch) = nand->data_desc_addr;
+	DCSR(nand->data_dma_ch) |= DCSR_RUN;
 }
 
 static void pxa3xx_nand_data_dma_irq(int channel, void *data)
 {
-	struct pxa3xx_nand_info *info = data;
+	struct pxa3xx_nand *nand = data;
 	uint32_t dcsr;
 
 	dcsr = DCSR(channel);
 	DCSR(channel) = dcsr;
 
 	if (dcsr & DCSR_BUSERR) {
-		info->retcode = ERR_DMABUSERR;
+		nand->retcode = ERR_DMABUSERR;
 	}
 
-	info->state = STATE_DMA_DONE;
-	enable_int(info, NDCR_INT_MASK);
-	nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
+	nand->state = STATE_DMA_DONE;
+	enable_int(nand, NDCR_INT_MASK);
+	nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
 }
 
 static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
-	struct pxa3xx_nand_info *info = devid;
-	unsigned int status, is_completed = 0;
+	struct pxa3xx_nand *nand = devid;
+	struct pxa3xx_nand_info *info;
+	unsigned int status, is_completed = 0, cs;
+	unsigned int ready, cmd_done, page_done, badblock_detect;
 
-	status = nand_readl(info, NDSR);
+	cs		= nand->chip_select;
+	ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
+	cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
+	page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
+	badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
+	info            = nand->info[cs];
 
+	status = nand_readl(nand, NDSR);
 	if (status & NDSR_DBERR)
-		info->retcode = ERR_DBERR;
+		nand->retcode = ERR_DBERR;
 	if (status & NDSR_SBERR)
-		info->retcode = ERR_SBERR;
+		nand->retcode = ERR_SBERR;
 	if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
 		/* whether use dma to transfer data */
-		if (info->use_dma) {
-			disable_int(info, NDCR_INT_MASK);
-			info->state = (status & NDSR_RDDREQ) ?
+		if (nand->use_dma) {
+			disable_int(nand, NDCR_INT_MASK);
+			nand->state = (status & NDSR_RDDREQ) ?
 				      STATE_DMA_READING : STATE_DMA_WRITING;
-			start_data_dma(info);
+			start_data_dma(nand);
 			goto NORMAL_IRQ_EXIT;
 		} else {
-			info->state = (status & NDSR_RDDREQ) ?
+			nand->state = (status & NDSR_RDDREQ) ?
 				      STATE_PIO_READING : STATE_PIO_WRITING;
-			handle_data_pio(info);
+			handle_data_pio(nand);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
-		info->state = STATE_CMD_DONE;
+	if (status & cmd_done) {
+		nand->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
-		info->is_ready = 1;
-		info->state = STATE_READY;
+	if (status & ready) {
+		nand->is_ready = 1;
+		nand->state = STATE_READY;
 	}
 
 	if (status & NDSR_WRCMDREQ) {
-		nand_writel(info, NDSR, NDSR_WRCMDREQ);
+		nand_writel(nand, NDSR, NDSR_WRCMDREQ);
 		status &= ~NDSR_WRCMDREQ;
-		info->state = STATE_CMD_HANDLE;
-		nand_writel(info, NDCB0, info->ndcb0);
-		nand_writel(info, NDCB0, info->ndcb1);
-		nand_writel(info, NDCB0, info->ndcb2);
+		nand->state = STATE_CMD_HANDLE;
+		nand_writel(nand, NDCB0, nand->ndcb0);
+		nand_writel(nand, NDCB0, nand->ndcb1);
+		nand_writel(nand, NDCB0, nand->ndcb2);
 	}
 
 	/* clear NDSR to let the controller exit the IRQ */
-	nand_writel(info, NDSR, status);
+	nand_writel(nand, NDSR, status);
 	if (is_completed)
-		complete(&info->cmd_complete);
+		complete(&nand->cmd_complete);
 NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
 	return 1;
 }
 
-static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
+static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	struct mtd_info *mtd;
+	struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
 
-	ndcb0 = 0;
+	mtd = get_mtd_by_info(info);
+	ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
 	/* reset data and oob column point to handle data */
-	info->buf_start		= 0;
-	info->buf_count		= 0;
-	info->oob_size		= 0;
-	info->use_ecc		= 0;
-	info->is_ready		= 0;
-	info->retcode		= ERR_NONE;
+	nand->buf_start		= 0;
+	nand->buf_count		= 0;
+	nand->oob_size		= 0;
+	nand->use_ecc		= 0;
+	nand->is_ready		= 0;
+	nand->retcode		= ERR_NONE;
+	nand->data_size		= 0;
+	nand->use_dma		= 0;
+	nand->command		= command;
 
 	switch (command) {
 	case NAND_CMD_READ0:
 	case NAND_CMD_PAGEPROG:
-		info->use_ecc = 1;
+		nand->use_ecc = 1;
 	case NAND_CMD_READOOB:
 		pxa3xx_set_datasize(info);
+		nand->oob_buff = nand->data_buff + nand->data_size;
+		nand->use_dma = use_dma;
 		break;
 	case NAND_CMD_SEQIN:
 		exec_cmd = 0;
 		break;
 	default:
-		info->ndcb1 = 0;
-		info->ndcb2 = 0;
+		nand->ndcb1 = 0;
+		nand->ndcb2 = 0;
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
+	nand->ndcb0 = ndcb0;
 	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
 				    + info->col_addr_cycles);
 
@@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	case NAND_CMD_READ0:
 		cmd = info->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
-			info->buf_start = mtd->writesize + column;
+			nand->buf_start = mtd->writesize + column;
 		else
-			info->buf_start = column;
+			nand->buf_start = column;
 
 		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
-			info->ndcb0 |= NDCB0_CMD_TYPE(0)
+			nand->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
 		else
-			info->ndcb0 |= NDCB0_CMD_TYPE(0)
+			nand->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| NDCB0_DBC
 					| addr_cycle
 					| cmd;
@@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
 		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
-			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
+			nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
-			info->ndcb2 = 0;
+			nand->ndcb2 = 0;
 		} else {
-			info->ndcb1 = ((page_addr & 0xFFFF) << 16)
+			nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
 					| (column & 0xFFFF);
 
 			if (page_addr & 0xFF0000)
-				info->ndcb2 = (page_addr & 0xFF0000) >> 16;
+				nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
 			else
-				info->ndcb2 = 0;
+				nand->ndcb2 = 0;
 		}
 
-		info->buf_count = mtd->writesize + mtd->oobsize;
-		memset(info->data_buff, 0xFF, info->buf_count);
+		nand->buf_count = mtd->writesize + mtd->oobsize;
+		memset(nand->data_buff, 0xFF, nand->buf_count);
 
 		break;
 
 	case NAND_CMD_PAGEPROG:
-		if (is_buf_blank(info->data_buff,
+		if (is_buf_blank(nand->data_buff,
 					(mtd->writesize + mtd->oobsize))) {
 			exec_cmd = 0;
 			break;
 		}
 
 		cmd = info->cmdset->program;
-		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
+		nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
 				| NDCB0_DBC
@@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_READID:
 		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
-		info->ndcb0 |= NDCB0_CMD_TYPE(3)
+		nand->buf_count = info->read_id_bytes;
+		nand->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
 
-		info->data_size = 8;
+		nand->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
 		cmd = info->cmdset->read_status;
-		info->buf_count = 1;
-		info->ndcb0 |= NDCB0_CMD_TYPE(4)
+		nand->buf_count = 1;
+		nand->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
 
-		info->data_size = 8;
+		nand->data_size = 8;
 		break;
 
 	case NAND_CMD_ERASE1:
 		cmd = info->cmdset->erase;
-		info->ndcb0 |= NDCB0_CMD_TYPE(2)
+		nand->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
 				| NDCB0_DBC
 				| cmd;
-		info->ndcb1 = page_addr;
-		info->ndcb2 = 0;
+		nand->ndcb1 = page_addr;
+		nand->ndcb2 = 0;
 
 		break;
 	case NAND_CMD_RESET:
 		cmd = info->cmdset->reset;
-		info->ndcb0 |= NDCB0_CMD_TYPE(5)
+		nand->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
 		break;
@@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 	int ret, exec_cmd;
 
 	/*
@@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (info->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
-	exec_cmd = prepare_command_pool(info, command, column, page_addr);
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (nand->chip_select != info->chip_select) {
+		nand->chip_select = info->chip_select;
+		nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
+		nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
+	}
+
+	nand->state = STATE_PREPARED;
+	exec_cmd = prepare_command_pool(nand, command, column, page_addr);
 	if (exec_cmd) {
-		init_completion(&info->cmd_complete);
-		pxa3xx_nand_start(info);
+		init_completion(&nand->cmd_complete);
+		pxa3xx_nand_start(nand);
 
-		ret = wait_for_completion_timeout(&info->cmd_complete,
+		ret = wait_for_completion_timeout(&nand->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
 			printk(KERN_ERR "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
-			pxa3xx_nand_stop(info);
+			pxa3xx_nand_stop(nand);
 		}
-		info->state = STATE_IDLE;
 	}
+	nand->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 
-	if (info->retcode == ERR_SBERR) {
+	if (nand->retcode == ERR_SBERR) {
 		switch (info->use_ecc) {
 		case 1:
 			mtd->ecc_stats.corrected++;
@@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		default:
 			break;
 		}
-	} else if (info->retcode == ERR_DBERR) {
+	} else if (nand->retcode == ERR_DBERR) {
 		/*
 		 * for blank page (all 0xff), HW will calculate its ECC as
 		 * 0, which is different from the ECC information within
 		 * OOB, ignore such double bit errors
 		 */
 		if (is_buf_blank(buf, mtd->writesize))
-			info->retcode = ERR_NONE;
+			nand->retcode = ERR_NONE;
 		else
 			mtd->ecc_stats.failed++;
 	}
@@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 	char retval = 0xFF;
 
-	if (info->buf_start < info->buf_count)
+	if (nand->buf_start < nand->buf_count)
 		/* Has just send a new command? */
-		retval = info->data_buff[info->buf_start++];
+		retval = nand->data_buff[nand->buf_start++];
 
 	return retval;
 }
@@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 	u16 retval = 0xFFFF;
 
-	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
-		retval = *((u16 *)(info->data_buff+info->buf_start));
-		info->buf_start += 2;
+	if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
+		retval = *((u16 *)(nand->data_buff+nand->buf_start));
+		nand->buf_start += 2;
 	}
 	return retval;
 }
@@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
-	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
+	struct pxa3xx_nand *nand = info->nand_data;
+	int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
 
-	memcpy(buf, info->data_buff + info->buf_start, real_len);
-	info->buf_start += real_len;
+	memcpy(buf, nand->data_buff + nand->buf_start, real_len);
+	nand->buf_start += real_len;
 }
 
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
-	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
+	struct pxa3xx_nand *nand = info->nand_data;
+	int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
 
-	memcpy(info->data_buff + info->buf_start, buf, real_len);
-	info->buf_start += real_len;
+	memcpy(nand->data_buff + nand->buf_start, buf, real_len);
+	nand->buf_start += real_len;
 }
 
 static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
@@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
-		if (info->retcode == ERR_NONE)
+		if (nand->retcode == ERR_NONE)
 			return 0;
 		else {
 			/*
@@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 				    const struct pxa3xx_nand_flash *f)
 {
-	struct platform_device *pdev = info->pdev;
+	struct pxa3xx_nand *nand = info->nand_data;
+	struct platform_device *pdev = nand->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
@@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
 	info->reg_ndcr = ndcr;
+	info->use_ecc = 1;
 
 	pxa3xx_nand_set_timing(info, f->timing);
 	return 0;
@@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	uint32_t ndcr = nand_readl(info, NDCR);
+	struct pxa3xx_nand *nand = info->nand_data;
+	uint32_t ndcr = nand_readl(nand, NDCR);
+
+	if (info->chip_select > 0) {
+		printk(KERN_ERR "We could not detect configure"
+				" if more than one cs is supported!!\n");
+		BUG();
+	}
 	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
 	/* set info fields needed to read id */
 	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
 	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
 	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
+	info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
 
 	return 0;
 }
@@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
  */
 #define MAX_BUFF_SIZE	PAGE_SIZE
 
-static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
+static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
 {
-	struct platform_device *pdev = info->pdev;
-	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
-
-	if (use_dma == 0) {
-		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
-		if (info->data_buff == NULL)
-			return -ENOMEM;
-		return 0;
-	}
-
-	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
-				&info->data_buff_phys, GFP_KERNEL);
-	if (info->data_buff == NULL) {
-		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
-		return -ENOMEM;
-	}
-
-	info->data_buff_size = MAX_BUFF_SIZE;
-	info->data_desc = (void *)info->data_buff + data_desc_offset;
-	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+	struct pxa3xx_nand *nand;
+	struct mtd_info *mtd;
 
-	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
-				pxa3xx_nand_data_dma_irq, info);
-	if (info->data_dma_ch < 0) {
-		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-				info->data_buff, info->data_buff_phys);
-		return info->data_dma_ch;
-	}
+	if (!info)
+		return;
 
-	return 0;
+	nand = info->nand_data;
+	mtd = get_mtd_by_info(info);
+	kfree(mtd);
+	nand->info[cs] = NULL;
 }
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand *nand = info->nand_data;
+	struct mtd_info *mtd = get_mtd_by_info(info);
 	struct nand_chip *chip = mtd->priv;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
+		return 0;
 	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
-	if (info->is_ready)
+	if (nand->is_ready)
 		return 1;
 	else
 		return 0;
@@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
-	struct platform_device *pdev = info->pdev;
+	struct pxa3xx_nand *nand = info->nand_data;
+	struct platform_device *pdev = nand->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
 	const struct pxa3xx_nand_flash *f = NULL;
@@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	uint64_t chipsize;
 	int i, ret, num;
 
+	nand->chip_select = info->chip_select;
 	if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
 	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		free_cs_resource(info, nand->chip_select);
+		printk(KERN_INFO "There is no nand chip on cs %d!\n",
+				nand->chip_select);
 
 		return -EINVAL;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
-	id = *((uint16_t *)(info->data_buff));
+	id = *((uint16_t *)(nand->data_buff));
 	if (id != 0)
 		printk(KERN_INFO "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
-
+		free_cs_resource(info, nand->chip_select);
 		return -EINVAL;
 	}
 
@@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
+		free_cs_resource(info, nand->chip_select);
 		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	if (pxa3xx_nand_config_flash(info, f)) {
+		printk(KERN_ERR "ERROR! Configure failed\n");
+		return -EINVAL;
+	}
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -950,13 +977,13 @@ KEEP_CONFIG:
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
+	nand->oob_buff = nand->data_buff + mtd->writesize;
 	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
-	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
 		info->row_addr_cycles = 3;
 	else
 		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
+	mtd->name = mtd_names[nand->chip_select];
 	chip->ecc.mode = NAND_ECC_HW;
 	chip->ecc.size = info->page_size;
 
@@ -967,51 +994,33 @@ KEEP_CONFIG:
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
+	struct pxa3xx_nand *nand;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
+	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
-			GFP_KERNEL);
-	if (!mtd) {
+	pdata = pdev->dev.platform_data;
+	nand = kzalloc(sizeof(struct mtd_info)
+			+ sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
+	if (!nand) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
-
-	spin_lock_init(&chip->controller->lock);
-	init_waitqueue_head(&chip->controller->wq);
-	info->clk = clk_get(&pdev->dev, NULL);
-	if (IS_ERR(info->clk)) {
+	nand->pdev = pdev;
+	nand->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(nand->clk)) {
 		dev_err(&pdev->dev, "failed to get nand clock\n");
-		ret = PTR_ERR(info->clk);
-		goto fail_free_mtd;
+		ret = PTR_ERR(nand->clk);
+		goto fail_alloc;
 	}
-	clk_enable(info->clk);
+	clk_enable(nand->clk);
 
 	r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 	if (r == NULL) {
@@ -1019,7 +1028,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 		ret = -ENXIO;
 		goto fail_put_clk;
 	}
-	info->drcmr_dat = r->start;
+	nand->drcmr_dat = r->start;
 
 	r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 	if (r == NULL) {
@@ -1027,7 +1036,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 		ret = -ENXIO;
 		goto fail_put_clk;
 	}
-	info->drcmr_cmd = r->start;
+	nand->drcmr_cmd = r->start;
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0) {
@@ -1050,81 +1059,147 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 		goto fail_put_clk;
 	}
 
-	info->mmio_base = ioremap(r->start, resource_size(r));
-	if (info->mmio_base == NULL) {
+	nand->mmio_base = ioremap(r->start, resource_size(r));
+	if (nand->mmio_base == NULL) {
 		dev_err(&pdev->dev, "ioremap() failed\n");
 		ret = -ENODEV;
 		goto fail_free_res;
 	}
-	info->mmio_phys = r->start;
-
-	ret = pxa3xx_nand_init_buff(info);
-	if (ret)
-		goto fail_free_io;
+	nand->mmio_phys = r->start;
 
 	/* initialize all interrupts to be disabled */
-	disable_int(info, NDSR_MASK);
+	disable_int(nand, NDSR_MASK);
 
 	ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED,
-			  pdev->name, info);
+			  pdev->name, nand);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to request IRQ\n");
-		goto fail_free_buf;
+		ret = -ENXIO;
+		goto fail_free_io;
+	}
+
+	platform_set_drvdata(pdev, nand);
+
+	spin_lock_init(&nand->controller.lock);
+	init_waitqueue_head(&nand->controller.wq);
+	for (cs = 0; cs < pdata->cs_num; cs++) {
+		mtd = kzalloc(sizeof(struct mtd_info)
+				+ sizeof(struct pxa3xx_nand_info),
+				GFP_KERNEL);
+		if (!mtd) {
+			dev_err(&pdev->dev, "failed to allocate memory\n");
+			ret = -ENOMEM;
+			goto fail_free_irq;
+		}
+
+		info = (struct pxa3xx_nand_info *)(&mtd[1]);
+		info->nand_data = nand;
+		info->chip_select = cs;
+		mtd->priv = info;
+		mtd->owner = THIS_MODULE;
+		nand->info[cs] = info;
+
+		chip = (struct nand_chip *)(&mtd[1]);
+		chip->controller        = &nand->controller;
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
 	}
 
-	platform_set_drvdata(pdev, info);
+	if (use_dma == 0) {
+		nand->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
+		if (nand->data_buff == NULL) {
+			ret = -ENOMEM;
+			goto fail_free_buf;
+		}
+		goto success_exit;
+	}
 
-	return info;
+	nand->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
+			&nand->data_buff_phys, GFP_KERNEL);
+	if (nand->data_buff == NULL) {
+		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
+		ret = -ENOMEM;
+		goto fail_free_buf;
+	}
 
+	nand->data_desc = (void *)nand->data_buff + data_desc_offset;
+	nand->data_desc_addr = nand->data_buff_phys + data_desc_offset;
+	nand->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
+			pxa3xx_nand_data_dma_irq, nand);
+	if (nand->data_dma_ch < 0) {
+		dev_err(&pdev->dev, "failed to request data dma\n");
+		ret = -ENXIO;
+		goto fail_free_dma_buf;
+	}
+success_exit:
+	return 0;
+
+fail_free_dma_buf:
+	dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
+			nand->data_buff, nand->data_buff_phys);
 fail_free_buf:
-	free_irq(irq, info);
-	if (use_dma) {
-		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
-			info->data_buff, info->data_buff_phys);
-	} else
-		kfree(info->data_buff);
+	for (cs = 0; cs < pdata->cs_num; cs++) {
+		info = nand->info[cs];
+		free_cs_resource(info, cs);
+	}
+fail_free_irq:
+	free_irq(irq, nand);
 fail_free_io:
-	iounmap(info->mmio_base);
+	iounmap(nand->mmio_base);
 fail_free_res:
 	release_mem_region(r->start, resource_size(r));
 fail_put_clk:
-	clk_disable(info->clk);
-	clk_put(info->clk);
-fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	clk_disable(nand->clk);
+	clk_put(nand->clk);
+fail_alloc:
+	kfree(nand);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
-	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand *nand = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
+	struct pxa3xx_nand_info *info;
+	struct mtd_info *mtd;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	platform_set_drvdata(pdev, NULL);
+	pdata = pdev->dev.platform_data;
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq >= 0)
-		free_irq(irq, info);
+		free_irq(irq, nand);
 	if (use_dma) {
-		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
-				info->data_buff, info->data_buff_phys);
+		pxa_free_dma(nand->data_dma_ch);
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
+				nand->data_buff, nand->data_buff_phys);
 	} else
-		kfree(info->data_buff);
+		kfree(nand->data_buff);
 
-	iounmap(info->mmio_base);
+	iounmap(nand->mmio_base);
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	clk_disable(info->clk);
-	clk_put(info->clk);
+	clk_disable(nand->clk);
+	clk_put(nand->clk);
 
-	if (mtd) {
+	for (cs = 0; cs < pdata->cs_num; cs++) {
+		info = nand->info[cs];
+		if (!info)
+			continue;
+		mtd = get_mtd_by_info(info);
 		mtd_device_unregister(mtd);
-		kfree(mtd);
+		free_cs_resource(info, cs);
 	}
 	return 0;
 }
@@ -1134,34 +1209,54 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 
+	struct pxa3xx_nand *nand;
+	struct mtd_info *mtd;
+	int cs, ret, nr_parts, probe_success;
+
+	probe_success = 0;
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
 		dev_err(&pdev->dev, "no platform data defined\n");
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
+	ret = alloc_nand_resource(pdev);
+	if (ret)
 		return -ENOMEM;
 
-	if (pxa3xx_nand_scan(info->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
-		pxa3xx_nand_remove(pdev);
-		return -ENODEV;
-	}
+	nand = platform_get_drvdata(pdev);
+	for (cs = 0; cs < pdata->cs_num; cs++) {
+		info = nand->info[cs];
+		mtd = get_mtd_by_info(info);
+		if (pxa3xx_nand_scan(mtd)) {
+			dev_err(&pdev->dev, "failed to scan nand\n");
+			continue;
+		}
+
+		ret = 0;
+		nr_parts = 0;
+		if (mtd_has_cmdlinepart()) {
+			const char *probes[] = { "cmdlinepart", NULL };
+			struct mtd_partition *parts;
 
-	if (mtd_has_cmdlinepart()) {
-		const char *probes[] = { "cmdlinepart", NULL };
-		struct mtd_partition *parts;
-		int nr_parts;
+			nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0);
 
-		nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0);
+			if (nr_parts)
+				ret = mtd_device_register(mtd, parts, nr_parts);
+		}
 
-		if (nr_parts)
-			return mtd_device_register(info->mtd, parts, nr_parts);
+		if (!nr_parts)
+			ret = mtd_device_register(mtd, pdata->parts[cs],
+					pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
 	}
 
-	return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
+	if (!probe_success) {
+		pxa3xx_nand_remove(pdev);
+		return -ENODEV;
+	} else
+		return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 	struct mtd_info *mtd = info->mtd;
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
+	nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-08 16:09   ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-08 16:09 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lei,


Some comments from a quick look...


On 06/22/11 06:17, Lei Wen wrote:

> Current pxa3xx_nand controller has two chip select which
> both be workable. This patch enable this feature.
>
> Update platform driver to support this feature.
>
> Another 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.

You should _also_ put this comment inside the pxa3xx_nand.h
may be even inside the pxa3xx_nand_platform_data structure,
so people would not have to search the git log to find this problem.

> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  arch/arm/mach-mmp/aspenite.c                 |    5 +-
>  arch/arm/mach-pxa/cm-x300.c                  |    5 +-
>  arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
>  arch/arm/mach-pxa/littleton.c                |    5 +-
>  arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
>  arch/arm/mach-pxa/raumfeld.c                 |    5 +-
>  arch/arm/mach-pxa/zylonite.c                 |    5 +-
>  arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |    8 +-
>  drivers/mtd/nand/pxa3xx_nand.c               |  735 +++++++++++++++-----------
>  9 files changed, 444 insertions(+), 338 deletions(-)
>
> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
> index 06b5fa8..b6589d6 100644
> --- a/arch/arm/mach-mmp/aspenite.c
> +++ b/arch/arm/mach-mmp/aspenite.c
> @@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
>  
>  static struct pxa3xx_nand_platform_data aspenite_nand_info = {
>  	.enable_arbiter	= 1,
> -	.parts		= aspenite_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
> +	.cs_num = 1,
> +	.parts[0]	= aspenite_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
>  };
>  
>  static struct i2c_board_info aspenite_i2c_info[] __initdata = {
> diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
> index b2248e7..d67eb7b 100644
> --- a/arch/arm/mach-pxa/cm-x300.c
> +++ b/arch/arm/mach-pxa/cm-x300.c
> @@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
>  	.enable_arbiter	= 1,
>  	.keep_config	= 1,
> -	.parts		= cm_x300_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= cm_x300_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
>  };
>  
>  static void __init cm_x300_init_nand(void)
> diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
> index 3f9be41..ff7a07b 100644
> --- a/arch/arm/mach-pxa/colibri-pxa3xx.c
> +++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
> @@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data colibri_nand_info = {
>  	.enable_arbiter	= 1,
>  	.keep_config	= 1,
> -	.parts		= colibri_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= colibri_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
>  };
>  
>  void __init colibri_pxa3xx_init_nand(void)
> diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
> index e5e326d..6eaf852 100644
> --- a/arch/arm/mach-pxa/littleton.c
> +++ b/arch/arm/mach-pxa/littleton.c
> @@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
>  
>  static struct pxa3xx_nand_platform_data littleton_nand_info = {
>  	.enable_arbiter	= 1,
> -	.parts		= littleton_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= littleton_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
>  };
>  
>  static void __init littleton_init_nand(void)
> diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
> index b5a8fd3..e7ce135 100644
> --- a/arch/arm/mach-pxa/mxm8x10.c
> +++ b/arch/arm/mach-pxa/mxm8x10.c
> @@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
>  };
>  
>  static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
> -	.enable_arbiter = 1,
> -	.keep_config = 1,
> -	.parts = mxm_8x10_nand_partitions,
> -	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
> +	.enable_arbiter	= 1,
> +	.keep_config	= 1,
> +	.cs_num		= 1,
> +	.parts[0]	= mxm_8x10_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
>  };
>  
>  static void __init mxm_8x10_nand_init(void)
> diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
> index d130f77..a54846d 100644
> --- a/arch/arm/mach-pxa/raumfeld.c
> +++ b/arch/arm/mach-pxa/raumfeld.c
> @@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
>  static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
>  	.enable_arbiter	= 1,
>  	.keep_config	= 1,
> -	.parts		= raumfeld_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= raumfeld_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
>  };
>  
>  /**
> diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
> index 5821185..ea4752a 100644
> --- a/arch/arm/mach-pxa/zylonite.c
> +++ b/arch/arm/mach-pxa/zylonite.c
> @@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
>  
>  static struct pxa3xx_nand_platform_data zylonite_nand_info = {
>  	.enable_arbiter	= 1,
> -	.parts		= zylonite_nand_partitions,
> -	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
> +	.cs_num		= 1,
> +	.parts[0]	= zylonite_nand_partitions,
> +	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
>  };
>  
>  static void __init zylonite_init_nand(void)
> diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> index 442301f..34a3f52 100644
> --- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
> @@ -41,6 +41,8 @@ struct pxa3xx_nand_flash {
>  	struct pxa3xx_nand_timing *timing;	/* NAND Flash timing */
>  };
>  
> +/* The max num of chip select current support */

/* The maximum number of chip selects currently supported */

> +#define NUM_CHIP_SELECT		(2)
>  struct pxa3xx_nand_platform_data {
>  
>  	/* the data flash bus is shared between the Static Memory
> @@ -52,8 +54,10 @@ struct pxa3xx_nand_platform_data {
>  	/* allow platform code to keep OBM/bootloader defined NFC config */
>  	int	keep_config;
>  
> -	const struct mtd_partition		*parts;
> -	unsigned int				nr_parts;
> +	/* indicate how many chip select would be used for this platform */

/* indicate how many chip selects will be used */

> +	int	cs_num;

This name is too confusing, I think even num_cs is better or cs_count?
Also, may be align it with the others?

> +	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;
> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index 30689cc..259b8d5 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -92,11 +92,13 @@
>  #define NDCB0_ADDR_CYC_SHIFT	(16)
>  
>  /* macros for registers read/write */
> -#define nand_writel(info, off, val)	\
> -	__raw_writel((val), (info)->mmio_base + (off))
> +#define nand_writel(nand, off, val)	\
> +	__raw_writel((val), (nand)->mmio_base + (off))
>  
> -#define nand_readl(info, off)		\
> -	__raw_readl((info)->mmio_base + (off))
> +#define nand_readl(nand, off)		\
> +	__raw_readl((nand)->mmio_base + (off))
> +#define get_mtd_by_info(info)		\
> +	(struct mtd_info *)((void *)info - sizeof(struct mtd_info))
>  
>  /* error code and state */
>  enum {
> @@ -110,6 +112,7 @@ enum {
>  
>  enum {
>  	STATE_IDLE = 0,
> +	STATE_PREPARED,
>  	STATE_CMD_HANDLE,
>  	STATE_DMA_READING,
>  	STATE_DMA_WRITING,
> @@ -123,63 +126,63 @@ enum {
>  struct pxa3xx_nand_info {
>  	struct nand_chip	nand_chip;
>  
> -	struct nand_hw_control	controller;
> -	struct platform_device	 *pdev;
>  	struct pxa3xx_nand_cmdset *cmdset;
> +	/* page size of attached chip */
> +	uint16_t		page_size;
> +	uint8_t			chip_select;
> +	uint8_t			use_ecc;
> +
> +	/* calculated from pxa3xx_nand_flash data */
> +	uint8_t			col_addr_cycles;
> +	uint8_t			row_addr_cycles;
> +	uint8_t			read_id_bytes;
> +
> +	/* cached register value */
> +	uint32_t		reg_ndcr;
> +	uint32_t		ndtr0cs0;
> +	uint32_t		ndtr1cs0;
>  
> +	void			*nand_data;
> +};
> +
> +struct pxa3xx_nand {
>  	struct clk		*clk;
>  	void __iomem		*mmio_base;
>  	unsigned long		mmio_phys;
> +	struct nand_hw_control	controller;
> +	struct completion	cmd_complete;
> +	struct platform_device	 *pdev;

please, align

>  
> -	unsigned int 		buf_start;
> -	unsigned int		buf_count;
> -
> -	struct mtd_info         *mtd;
>  	/* DMA information */
>  	int			drcmr_dat;
>  	int			drcmr_cmd;
> -
> -	unsigned char		*data_buff;
> -	unsigned char		*oob_buff;
> -	dma_addr_t 		data_buff_phys;
> -	size_t			data_buff_size;
>  	int 			data_dma_ch;
> -	struct pxa_dma_desc	*data_desc;
> +	dma_addr_t		data_buff_phys;
>  	dma_addr_t 		data_desc_addr;
> +	struct pxa_dma_desc	*data_desc;
>  
> -	uint32_t		reg_ndcr;
> -
> -	/* saved column/page_addr during CMD_SEQIN */
> -	int			seqin_column;
> -	int			seqin_page_addr;
> +	struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
> +	uint32_t		command;
> +	uint16_t		data_size;	/* data size in FIFO */
> +	uint16_t		oob_size;
> +	unsigned char		*data_buff;
> +	unsigned char		*oob_buff;
> +	uint32_t		buf_start;
> +	uint32_t		buf_count;
>  
>  	/* relate to the command */
>  	unsigned int		state;
> -
> +	uint8_t			chip_select;
>  	int			use_ecc;	/* use HW ECC ? */
>  	int			use_dma;	/* use DMA ? */
>  	int			is_ready;
> -
> -	unsigned int		page_size;	/* page size of attached chip */
> -	unsigned int		data_size;	/* data size in FIFO */
>  	int 			retcode;
> -	struct completion 	cmd_complete;
>  
>  	/* generated NDCBx register values */
> +	uint8_t			total_cmds;
>  	uint32_t		ndcb0;
>  	uint32_t		ndcb1;
>  	uint32_t		ndcb2;
> -
> -	/* timing calcuted from setting */
> -	uint32_t		ndtr0cs0;
> -	uint32_t		ndtr1cs0;
> -
> -	/* calculated from pxa3xx_nand_flash data */
> -	size_t		oob_size;
> -	size_t		read_id_bytes;
> -
> -	unsigned int	col_addr_cycles;
> -	unsigned int	row_addr_cycles;
>  };

It looks like if you switch the names of the structures above,
then the patch will be much shorter and cleaner,
but will it make structures meaning confusion?

>  
>  static int use_dma = 1;
> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>  /* Define a default flash type setting serve as flash detecting only */
>  #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>  
> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>  
>  #define NDTR0_tCH(c)	(min((c), 7) << 19)
>  #define NDTR0_tCS(c)	(min((c), 7) << 16)
> @@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>  static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>  				   const struct pxa3xx_nand_timing *t)
>  {
> -	unsigned long nand_clk = clk_get_rate(info->clk);
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	unsigned long nand_clk;
>  	uint32_t ndtr0, ndtr1;
>  
> +	nand_clk = clk_get_rate(nand->clk);
>  	ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
>  		NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
>  		NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
> @@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>  
>  	info->ndtr0cs0 = ndtr0;
>  	info->ndtr1cs0 = ndtr1;
> -	nand_writel(info, NDTR0CS0, ndtr0);
> -	nand_writel(info, NDTR1CS0, ndtr1);
> +	nand_writel(nand, NDTR0CS0, ndtr0);
> +	nand_writel(nand, NDTR1CS0, ndtr1);
>  }
>  
>  static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>  {
> +	struct pxa3xx_nand *nand = info->nand_data;
>  	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
>  
> -	info->data_size = info->page_size;
> +	nand->data_size = info->page_size;
>  	if (!oob_enable) {
> -		info->oob_size = 0;
> +		nand->oob_size = 0;
>  		return;
>  	}
>  
>  	switch (info->page_size) {
>  	case 2048:
> -		info->oob_size = (info->use_ecc) ? 40 : 64;
> +		nand->oob_size = (info->use_ecc) ? 40 : 64;
>  		break;
>  	case 512:
> -		info->oob_size = (info->use_ecc) ? 8 : 16;
> +		nand->oob_size = (info->use_ecc) ? 8 : 16;
>  		break;
>  	}
>  }
> @@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>   * 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_info *info)
> +static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
>  {
> +	struct pxa3xx_nand_info *info;
>  	uint32_t ndcr;
>  
> +	info = nand->info[nand->chip_select];
>  	ndcr = info->reg_ndcr;
> -	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
> -	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
> +	ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
> +	ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
>  	ndcr |= NDCR_ND_RUN;
>  
>  	/* clear status bits and run */
> -	nand_writel(info, NDCR, 0);
> -	nand_writel(info, NDSR, NDSR_MASK);
> -	nand_writel(info, NDCR, ndcr);
> +	nand_writel(nand, NDCR, 0);
> +	nand_writel(nand, NDSR, NDSR_MASK);
> +	nand_writel(nand, NDCR, ndcr);
>  }
>  
> -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
> +static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
>  {
>  	uint32_t ndcr;
>  	int timeout = NAND_STOP_DELAY;
>  
>  	/* wait RUN bit in NDCR become 0 */
> -	ndcr = nand_readl(info, NDCR);
> +	ndcr = nand_readl(nand, NDCR);
>  	while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
> -		ndcr = nand_readl(info, NDCR);
> +		ndcr = nand_readl(nand, NDCR);
>  		udelay(1);
>  	}
>  
>  	if (timeout <= 0) {
>  		ndcr &= ~NDCR_ND_RUN;
> -		nand_writel(info, NDCR, ndcr);
> +		nand_writel(nand, NDCR, ndcr);
>  	}
>  	/* clear status bits */
> -	nand_writel(info, NDSR, NDSR_MASK);
> +	nand_writel(nand, NDSR, NDSR_MASK);
>  }
>  
> -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> +static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>  {
>  	uint32_t ndcr;
>  
> -	ndcr = nand_readl(info, NDCR);
> -	nand_writel(info, NDCR, ndcr & ~int_mask);
> +	ndcr = nand_readl(nand, NDCR);
> +	nand_writel(nand, NDCR, ndcr & ~int_mask);
>  }
>  
> -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>  {
>  	uint32_t ndcr;
>  
> -	ndcr = nand_readl(info, NDCR);
> -	nand_writel(info, NDCR, ndcr | int_mask);
> +	ndcr = nand_readl(nand, NDCR);
> +	nand_writel(nand, NDCR, ndcr | int_mask);
>  }
>  
> -static void handle_data_pio(struct pxa3xx_nand_info *info)
> +static void handle_data_pio(struct pxa3xx_nand *nand)
>  {
> -	switch (info->state) {
> +	switch (nand->state) {
>  	case STATE_PIO_WRITING:
> -		__raw_writesl(info->mmio_base + NDDB, info->data_buff,
> -				DIV_ROUND_UP(info->data_size, 4));
> -		if (info->oob_size > 0)
> -			__raw_writesl(info->mmio_base + NDDB, info->oob_buff,
> -					DIV_ROUND_UP(info->oob_size, 4));
> +		__raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
> +				DIV_ROUND_UP(nand->data_size, 4));
> +		if (nand->oob_size > 0)
> +			__raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
> +					DIV_ROUND_UP(nand->oob_size, 4));
>  		break;
>  	case STATE_PIO_READING:
> -		__raw_readsl(info->mmio_base + NDDB, info->data_buff,
> -				DIV_ROUND_UP(info->data_size, 4));
> -		if (info->oob_size > 0)
> -			__raw_readsl(info->mmio_base + NDDB, info->oob_buff,
> -					DIV_ROUND_UP(info->oob_size, 4));
> +		__raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
> +				DIV_ROUND_UP(nand->data_size, 4));
> +		if (nand->oob_size > 0)
> +			__raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
> +					DIV_ROUND_UP(nand->oob_size, 4));
>  		break;
>  	default:
>  		printk(KERN_ERR "%s: invalid state %d\n", __func__,
> -				info->state);
> +				nand->state);
>  		BUG();
>  	}
>  }
>  
> -static void start_data_dma(struct pxa3xx_nand_info *info)
> +static void start_data_dma(struct pxa3xx_nand *nand)
>  {
> -	struct pxa_dma_desc *desc = info->data_desc;
> -	int dma_len = ALIGN(info->data_size + info->oob_size, 32);
> +	struct pxa_dma_desc *desc = nand->data_desc;
> +	int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
>  
>  	desc->ddadr = DDADR_STOP;
>  	desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
>  
> -	switch (info->state) {
> +	switch (nand->state) {
>  	case STATE_DMA_WRITING:
> -		desc->dsadr = info->data_buff_phys;
> -		desc->dtadr = info->mmio_phys + NDDB;
> +		desc->dsadr = nand->data_buff_phys;
> +		desc->dtadr = nand->mmio_phys + NDDB;
>  		desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
>  		break;
>  	case STATE_DMA_READING:
> -		desc->dtadr = info->data_buff_phys;
> -		desc->dsadr = info->mmio_phys + NDDB;
> +		desc->dtadr = nand->data_buff_phys;
> +		desc->dsadr = nand->mmio_phys + NDDB;
>  		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
>  		break;
>  	default:
>  		printk(KERN_ERR "%s: invalid state %d\n", __func__,
> -				info->state);
> +				nand->state);
>  		BUG();
>  	}
>  
> -	DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
> -	DDADR(info->data_dma_ch) = info->data_desc_addr;
> -	DCSR(info->data_dma_ch) |= DCSR_RUN;
> +	DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
> +	DDADR(nand->data_dma_ch) = nand->data_desc_addr;
> +	DCSR(nand->data_dma_ch) |= DCSR_RUN;
>  }
>  
>  static void pxa3xx_nand_data_dma_irq(int channel, void *data)
>  {
> -	struct pxa3xx_nand_info *info = data;
> +	struct pxa3xx_nand *nand = data;
>  	uint32_t dcsr;
>  
>  	dcsr = DCSR(channel);
>  	DCSR(channel) = dcsr;
>  
>  	if (dcsr & DCSR_BUSERR) {
> -		info->retcode = ERR_DMABUSERR;
> +		nand->retcode = ERR_DMABUSERR;
>  	}
>  
> -	info->state = STATE_DMA_DONE;
> -	enable_int(info, NDCR_INT_MASK);
> -	nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
> +	nand->state = STATE_DMA_DONE;
> +	enable_int(nand, NDCR_INT_MASK);
> +	nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>  }
>  
>  static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>  {
> -	struct pxa3xx_nand_info *info = devid;
> -	unsigned int status, is_completed = 0;
> +	struct pxa3xx_nand *nand = devid;
> +	struct pxa3xx_nand_info *info;
> +	unsigned int status, is_completed = 0, cs;
> +	unsigned int ready, cmd_done, page_done, badblock_detect;
>  
> -	status = nand_readl(info, NDSR);
> +	cs		= nand->chip_select;
> +	ready           = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
> +	cmd_done        = (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
> +	page_done       = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
> +	badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;

This is confusing... do you use to ?: operator for differentiating between
cs = 0 and cs = 1?
I think this is a bad idea.
Moreover, the use of ?: is discouraged among the kernel.

> +	info            = nand->info[cs];
>  
> +	status = nand_readl(nand, NDSR);
>  	if (status & NDSR_DBERR)
> -		info->retcode = ERR_DBERR;
> +		nand->retcode = ERR_DBERR;
>  	if (status & NDSR_SBERR)
> -		info->retcode = ERR_SBERR;
> +		nand->retcode = ERR_SBERR;
>  	if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
>  		/* whether use dma to transfer data */
> -		if (info->use_dma) {
> -			disable_int(info, NDCR_INT_MASK);
> -			info->state = (status & NDSR_RDDREQ) ?
> +		if (nand->use_dma) {
> +			disable_int(nand, NDCR_INT_MASK);
> +			nand->state = (status & NDSR_RDDREQ) ?
>  				      STATE_DMA_READING : STATE_DMA_WRITING;
> -			start_data_dma(info);
> +			start_data_dma(nand);
>  			goto NORMAL_IRQ_EXIT;
>  		} else {
> -			info->state = (status & NDSR_RDDREQ) ?
> +			nand->state = (status & NDSR_RDDREQ) ?
>  				      STATE_PIO_READING : STATE_PIO_WRITING;
> -			handle_data_pio(info);
> +			handle_data_pio(nand);
>  		}
>  	}
> -	if (status & NDSR_CS0_CMDD) {
> -		info->state = STATE_CMD_DONE;
> +	if (status & cmd_done) {
> +		nand->state = STATE_CMD_DONE;
>  		is_completed = 1;
>  	}
> -	if (status & NDSR_FLASH_RDY) {
> -		info->is_ready = 1;
> -		info->state = STATE_READY;
> +	if (status & ready) {
> +		nand->is_ready = 1;
> +		nand->state = STATE_READY;
>  	}
>  
>  	if (status & NDSR_WRCMDREQ) {
> -		nand_writel(info, NDSR, NDSR_WRCMDREQ);
> +		nand_writel(nand, NDSR, NDSR_WRCMDREQ);
>  		status &= ~NDSR_WRCMDREQ;
> -		info->state = STATE_CMD_HANDLE;
> -		nand_writel(info, NDCB0, info->ndcb0);
> -		nand_writel(info, NDCB0, info->ndcb1);
> -		nand_writel(info, NDCB0, info->ndcb2);
> +		nand->state = STATE_CMD_HANDLE;
> +		nand_writel(nand, NDCB0, nand->ndcb0);
> +		nand_writel(nand, NDCB0, nand->ndcb1);
> +		nand_writel(nand, NDCB0, nand->ndcb2);
>  	}
>  
>  	/* clear NDSR to let the controller exit the IRQ */
> -	nand_writel(info, NDSR, status);
> +	nand_writel(nand, NDSR, status);
>  	if (is_completed)
> -		complete(&info->cmd_complete);
> +		complete(&nand->cmd_complete);
>  NORMAL_IRQ_EXIT:
>  	return IRQ_HANDLED;
>  }
>  
> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
> -{
> -	struct pxa3xx_nand_info *info = mtd->priv;
> -	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
> -}
> -
>  static inline int is_buf_blank(uint8_t *buf, size_t len)
>  {
>  	for (; len > 0; len--)
> @@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
>  	return 1;
>  }
>  
> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>  		uint16_t column, int page_addr)
>  {
>  	uint16_t cmd;
>  	int addr_cycle, exec_cmd, ndcb0;
> -	struct mtd_info *mtd = info->mtd;
> +	struct mtd_info *mtd;
> +	struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>  
> -	ndcb0 = 0;
> +	mtd = get_mtd_by_info(info);
> +	ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;

This one is confusing too...
Besides, you don't need the parenthesis.

>  	addr_cycle = 0;
>  	exec_cmd = 1;
>  
>  	/* reset data and oob column point to handle data */
> -	info->buf_start		= 0;
> -	info->buf_count		= 0;
> -	info->oob_size		= 0;
> -	info->use_ecc		= 0;
> -	info->is_ready		= 0;
> -	info->retcode		= ERR_NONE;
> +	nand->buf_start		= 0;
> +	nand->buf_count		= 0;
> +	nand->oob_size		= 0;
> +	nand->use_ecc		= 0;
> +	nand->is_ready		= 0;
> +	nand->retcode		= ERR_NONE;
> +	nand->data_size		= 0;
> +	nand->use_dma		= 0;
> +	nand->command		= command;
>  
>  	switch (command) {
>  	case NAND_CMD_READ0:
>  	case NAND_CMD_PAGEPROG:
> -		info->use_ecc = 1;
> +		nand->use_ecc = 1;
>  	case NAND_CMD_READOOB:
>  		pxa3xx_set_datasize(info);
> +		nand->oob_buff = nand->data_buff + nand->data_size;
> +		nand->use_dma = use_dma;
>  		break;
>  	case NAND_CMD_SEQIN:
>  		exec_cmd = 0;
>  		break;
>  	default:
> -		info->ndcb1 = 0;
> -		info->ndcb2 = 0;
> +		nand->ndcb1 = 0;
> +		nand->ndcb2 = 0;
>  		break;
>  	}
>  
> -	info->ndcb0 = ndcb0;
> +	nand->ndcb0 = ndcb0;
>  	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
>  				    + info->col_addr_cycles);
>  
> @@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  	case NAND_CMD_READ0:
>  		cmd = info->cmdset->read1;
>  		if (command == NAND_CMD_READOOB)
> -			info->buf_start = mtd->writesize + column;
> +			nand->buf_start = mtd->writesize + column;
>  		else
> -			info->buf_start = column;
> +			nand->buf_start = column;
>  
>  		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
> -			info->ndcb0 |= NDCB0_CMD_TYPE(0)
> +			nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>  					| addr_cycle
>  					| (cmd & NDCB0_CMD1_MASK);
>  		else
> -			info->ndcb0 |= NDCB0_CMD_TYPE(0)
> +			nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>  					| NDCB0_DBC
>  					| addr_cycle
>  					| cmd;
> @@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  	case NAND_CMD_SEQIN:
>  		/* small page addr setting */
>  		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
> -			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
> +			nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>  					| (column & 0xFF);
>  
> -			info->ndcb2 = 0;
> +			nand->ndcb2 = 0;
>  		} else {
> -			info->ndcb1 = ((page_addr & 0xFFFF) << 16)
> +			nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
>  					| (column & 0xFFFF);
>  
>  			if (page_addr & 0xFF0000)
> -				info->ndcb2 = (page_addr & 0xFF0000) >> 16;
> +				nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
>  			else
> -				info->ndcb2 = 0;
> +				nand->ndcb2 = 0;
>  		}
>  
> -		info->buf_count = mtd->writesize + mtd->oobsize;
> -		memset(info->data_buff, 0xFF, info->buf_count);
> +		nand->buf_count = mtd->writesize + mtd->oobsize;
> +		memset(nand->data_buff, 0xFF, nand->buf_count);
>  
>  		break;
>  
>  	case NAND_CMD_PAGEPROG:
> -		if (is_buf_blank(info->data_buff,
> +		if (is_buf_blank(nand->data_buff,
>  					(mtd->writesize + mtd->oobsize))) {
>  			exec_cmd = 0;
>  			break;
>  		}
>  
>  		cmd = info->cmdset->program;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>  				| NDCB0_AUTO_RS
>  				| NDCB0_ST_ROW_EN
>  				| NDCB0_DBC
> @@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>  
>  	case NAND_CMD_READID:
>  		cmd = info->cmdset->read_id;
> -		info->buf_count = info->read_id_bytes;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(3)
> +		nand->buf_count = info->read_id_bytes;
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(3)
>  				| NDCB0_ADDR_CYC(1)
>  				| cmd;
>  
> -		info->data_size = 8;
> +		nand->data_size = 8;
>  		break;
>  	case NAND_CMD_STATUS:
>  		cmd = info->cmdset->read_status;
> -		info->buf_count = 1;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(4)
> +		nand->buf_count = 1;
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(4)
>  				| NDCB0_ADDR_CYC(1)
>  				| cmd;
>  
> -		info->data_size = 8;
> +		nand->data_size = 8;
>  		break;
>  
>  	case NAND_CMD_ERASE1:
>  		cmd = info->cmdset->erase;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(2)
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(2)
>  				| NDCB0_AUTO_RS
>  				| NDCB0_ADDR_CYC(3)
>  				| NDCB0_DBC
>  				| cmd;
> -		info->ndcb1 = page_addr;
> -		info->ndcb2 = 0;
> +		nand->ndcb1 = page_addr;
> +		nand->ndcb2 = 0;
>  
>  		break;
>  	case NAND_CMD_RESET:
>  		cmd = info->cmdset->reset;
> -		info->ndcb0 |= NDCB0_CMD_TYPE(5)
> +		nand->ndcb0 |= NDCB0_CMD_TYPE(5)
>  				| cmd;
>  
>  		break;
> @@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>  				int column, int page_addr)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  	int ret, exec_cmd;
>  
>  	/*
> @@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>  	if (info->reg_ndcr & NDCR_DWIDTH_M)
>  		column /= 2;
>  
> -	exec_cmd = prepare_command_pool(info, command, column, page_addr);
> +	/*
> +	 * There may be different NAND chip hooked to
> +	 * different chip select, so check whether
> +	 * chip select has been changed, if yes, reset the timing
> +	 */
> +	if (nand->chip_select != info->chip_select) {
> +		nand->chip_select = info->chip_select;
> +		nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
> +		nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
> +	}
> +
> +	nand->state = STATE_PREPARED;
> +	exec_cmd = prepare_command_pool(nand, command, column, page_addr);
>  	if (exec_cmd) {
> -		init_completion(&info->cmd_complete);
> -		pxa3xx_nand_start(info);
> +		init_completion(&nand->cmd_complete);
> +		pxa3xx_nand_start(nand);
>  
> -		ret = wait_for_completion_timeout(&info->cmd_complete,
> +		ret = wait_for_completion_timeout(&nand->cmd_complete,
>  				CHIP_DELAY_TIMEOUT);
>  		if (!ret) {
>  			printk(KERN_ERR "Wait time out!!!\n");
>  			/* Stop State Machine for next command cycle */
> -			pxa3xx_nand_stop(info);
> +			pxa3xx_nand_stop(nand);
>  		}
> -		info->state = STATE_IDLE;
>  	}
> +	nand->state = STATE_IDLE;
>  }
>  
>  static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
> @@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>  		struct nand_chip *chip, uint8_t *buf, int page)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  
>  	chip->read_buf(mtd, buf, mtd->writesize);
>  	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>  
> -	if (info->retcode == ERR_SBERR) {
> +	if (nand->retcode == ERR_SBERR) {
>  		switch (info->use_ecc) {
>  		case 1:
>  			mtd->ecc_stats.corrected++;
> @@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>  		default:
>  			break;
>  		}
> -	} else if (info->retcode == ERR_DBERR) {
> +	} else if (nand->retcode == ERR_DBERR) {
>  		/*
>  		 * for blank page (all 0xff), HW will calculate its ECC as
>  		 * 0, which is different from the ECC information within
>  		 * OOB, ignore such double bit errors
>  		 */
>  		if (is_buf_blank(buf, mtd->writesize))
> -			info->retcode = ERR_NONE;
> +			nand->retcode = ERR_NONE;
>  		else
>  			mtd->ecc_stats.failed++;
>  	}
> @@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>  static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  	char retval = 0xFF;
>  
> -	if (info->buf_start < info->buf_count)
> +	if (nand->buf_start < nand->buf_count)
>  		/* Has just send a new command? */
> -		retval = info->data_buff[info->buf_start++];
> +		retval = nand->data_buff[nand->buf_start++];
>  
>  	return retval;
>  }
> @@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>  static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  	u16 retval = 0xFFFF;
>  
> -	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
> -		retval = *((u16 *)(info->data_buff+info->buf_start));
> -		info->buf_start += 2;
> +	if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
> +		retval = *((u16 *)(nand->data_buff+nand->buf_start));
> +		nand->buf_start += 2;
>  	}
>  	return retval;
>  }
> @@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>  static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> -	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>  
> -	memcpy(buf, info->data_buff + info->buf_start, real_len);
> -	info->buf_start += real_len;
> +	memcpy(buf, nand->data_buff + nand->buf_start, real_len);
> +	nand->buf_start += real_len;
>  }
>  
>  static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
>  		const uint8_t *buf, int len)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> -	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>  
> -	memcpy(info->data_buff + info->buf_start, buf, real_len);
> -	info->buf_start += real_len;
> +	memcpy(nand->data_buff + nand->buf_start, buf, real_len);
> +	nand->buf_start += real_len;
>  }
>  
>  static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
> @@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>  static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> +	struct pxa3xx_nand *nand = info->nand_data;
>  
>  	/* pxa3xx_nand_send_command has waited for command complete */
>  	if (this->state == FL_WRITING || this->state == FL_ERASING) {
> -		if (info->retcode == ERR_NONE)
> +		if (nand->retcode == ERR_NONE)
>  			return 0;
>  		else {
>  			/*
> @@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>  static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>  				    const struct pxa3xx_nand_flash *f)
>  {
> -	struct platform_device *pdev = info->pdev;
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	struct platform_device *pdev = nand->pdev;
>  	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>  	uint32_t ndcr = 0x0; /* enable all interrupts */
>  
> @@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>  	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>  
>  	info->reg_ndcr = ndcr;
> +	info->use_ecc = 1;
>  
>  	pxa3xx_nand_set_timing(info, f->timing);
>  	return 0;
> @@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>  
>  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>  {
> -	uint32_t ndcr = nand_readl(info, NDCR);
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	uint32_t ndcr = nand_readl(nand, NDCR);
> +
> +	if (info->chip_select > 0) {
> +		printk(KERN_ERR "We could not detect configure"
> +				" if more than one cs is supported!!\n");
> +		BUG();

like Daniel already noticed, may be dev_err() is enough?

> +	}
>  	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
>  	/* set info fields needed to read id */
>  	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
>  	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
>  	info->cmdset = &default_cmdset;
>  
> -	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
> -	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
> +	info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
> +	info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
>  
>  	return 0;
>  }
> @@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>   */
>  #define MAX_BUFF_SIZE	PAGE_SIZE
>  
> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
>  {
> -	struct platform_device *pdev = info->pdev;
> -	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
> -
> -	if (use_dma == 0) {
> -		info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> -		if (info->data_buff == NULL)
> -			return -ENOMEM;
> -		return 0;
> -	}
> -
> -	info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> -				&info->data_buff_phys, GFP_KERNEL);
> -	if (info->data_buff == NULL) {
> -		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> -		return -ENOMEM;
> -	}
> -
> -	info->data_buff_size = MAX_BUFF_SIZE;
> -	info->data_desc = (void *)info->data_buff + data_desc_offset;
> -	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
> +	struct pxa3xx_nand *nand;
> +	struct mtd_info *mtd;
>  
> -	info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> -				pxa3xx_nand_data_dma_irq, info);
> -	if (info->data_dma_ch < 0) {
> -		dev_err(&pdev->dev, "failed to request data dma\n");
> -		dma_free_coherent(&pdev->dev, info->data_buff_size,
> -				info->data_buff, info->data_buff_phys);
> -		return info->data_dma_ch;
> -	}
> +	if (!info)
> +		return;
>  
> -	return 0;
> +	nand = info->nand_data;
> +	mtd = get_mtd_by_info(info);
> +	kfree(mtd);
> +	nand->info[cs] = NULL;
>  }
>  
>  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>  {
> -	struct mtd_info *mtd = info->mtd;
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	struct mtd_info *mtd = get_mtd_by_info(info);
>  	struct nand_chip *chip = mtd->priv;
>  
>  	/* use the common timing to make a try */
> -	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
> +	if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
> +		return 0;
>  	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
> -	if (info->is_ready)
> +	if (nand->is_ready)
>  		return 1;
>  	else
>  		return 0;

I think it is time to change this function return convention to propagate
errors and not just 0 or 1, (may be in separate patch) what do you think?

> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>  static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  {
>  	struct pxa3xx_nand_info *info = mtd->priv;
> -	struct platform_device *pdev = info->pdev;
> +	struct pxa3xx_nand *nand = info->nand_data;
> +	struct platform_device *pdev = nand->pdev;
>  	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>  	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>  	const struct pxa3xx_nand_flash *f = NULL;
> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  	uint64_t chipsize;
>  	int i, ret, num;
>  
> +	nand->chip_select = info->chip_select;
>  	if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>  		goto KEEP_CONFIG;
>  
>  	ret = pxa3xx_nand_sensing(info);
>  	if (!ret) {
> -		kfree(mtd);
> -		info->mtd = NULL;
> -		printk(KERN_INFO "There is no nand chip on cs 0!\n");
> +		free_cs_resource(info, nand->chip_select);
> +		printk(KERN_INFO "There is no nand chip on cs %d!\n",
> +				nand->chip_select);
>  
>  		return -EINVAL;
>  	}
>  
>  	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
> -	id = *((uint16_t *)(info->data_buff));
> +	id = *((uint16_t *)(nand->data_buff));
>  	if (id != 0)
>  		printk(KERN_INFO "Detect a flash id %x\n", id);
>  	else {
> -		kfree(mtd);
> -		info->mtd = NULL;
> -		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");

Is this warning no longer needed?

> -
> +		free_cs_resource(info, nand->chip_select);
>  		return -EINVAL;
>  	}
>  
> @@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>  	}
>  
>  	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
> -		kfree(mtd);
> -		info->mtd = NULL;
> +		free_cs_resource(info, nand->chip_select);
>  		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
>  
>  		return -EINVAL;
>  	}
>  
> -	pxa3xx_nand_config_flash(info, f);
> +	if (pxa3xx_nand_config_flash(info, f)) {
> +		printk(KERN_ERR "ERROR! Configure failed\n");
> +		return -EINVAL;
> +	}

Although, the pxa3xx_nand_config_flash() returns only 0 or -EINVAL,
it is better to propagate its return value.

>  	pxa3xx_flash_ids[0].name = f->name;
>  	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
>  	pxa3xx_flash_ids[0].pagesize = f->page_size;
> @@ -950,13 +977,13 @@ KEEP_CONFIG:
>  	if (nand_scan_ident(mtd, 1, def))
>  		return -ENODEV;
>  	/* calculate addressing information */
> +	nand->oob_buff = nand->data_buff + mtd->writesize;
>  	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
> -	info->oob_buff = info->data_buff + mtd->writesize;
>  	if ((mtd->size >> chip->page_shift) > 65536)
>  		info->row_addr_cycles = 3;
>  	else
>  		info->row_addr_cycles = 2;
> -	mtd->name = mtd_names[0];
> +	mtd->name = mtd_names[nand->chip_select];
>  	chip->ecc.mode = NAND_ECC_HW;
>  	chip->ecc.size = info->page_size;
>  
> @@ -967,51 +994,33 @@ KEEP_CONFIG:
>  	return nand_scan_tail(mtd);
>  }
>  
> -static
> -struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
> +static int alloc_nand_resource(struct platform_device *pdev)
>  {
> +	struct pxa3xx_nand_platform_data *pdata;
>  	struct pxa3xx_nand_info *info;
>  	struct nand_chip *chip;
>  	struct mtd_info *mtd;
> +	struct pxa3xx_nand *nand;
>  	struct resource *r;
> -	int ret, irq;
> +	int ret, irq, cs;
> +	int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>  
> -	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
> -			GFP_KERNEL);
> -	if (!mtd) {
> +	pdata = pdev->dev.platform_data;
> +	nand = kzalloc(sizeof(struct mtd_info)
> +			+ sizeof(struct pxa3xx_nand_info), GFP_KERNEL);
> +	if (!nand) {
>  		dev_err(&pdev->dev, "failed to allocate memory\n");
> -		return NULL;
> +		return -ENOMEM;
>  	}
>  
> -	info = (struct pxa3xx_nand_info *)(&mtd[1]);
> -	chip = (struct nand_chip *)(&mtd[1]);
> -	info->pdev = pdev;
> -	info->mtd = mtd;
> -	mtd->priv = info;
> -	mtd->owner = THIS_MODULE;
> -
> -	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
> -	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
> -	chip->controller        = &info->controller;
> -	chip->waitfunc		= pxa3xx_nand_waitfunc;
> -	chip->select_chip	= pxa3xx_nand_select_chip;
> -	chip->dev_ready		= pxa3xx_nand_dev_ready;
> -	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
> -	chip->read_word		= pxa3xx_nand_read_word;
> -	chip->read_byte		= pxa3xx_nand_read_byte;
> -	chip->read_buf		= pxa3xx_nand_read_buf;
> -	chip->write_buf		= pxa3xx_nand_write_buf;
> -	chip->verify_buf	= pxa3xx_nand_verify_buf;
> -
> -	spin_lock_init(&chip->controller->lock);
> -	init_waitqueue_head(&chip->controller->wq);
> -	info->clk = clk_get(&pdev->dev, NULL);
> -	if (IS_ERR(info->clk)) {
> +	nand->pdev = pdev;
> +	nand->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(nand->clk)) {
>  		dev_err(&pdev->dev, "failed to get nand clock\n");
> -		ret = PTR_ERR(info->clk);
> -		goto fail_free_mtd;
> +		ret = PTR_ERR(nand->clk);
> +		goto fail_alloc;
>  	}
> -	clk_enable(info->clk);
> +	clk_enable(nand->clk);
>  
>  	r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>  	if (r == NULL) {
> @@ -1019,7 +1028,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>  		ret = -ENXIO;
>  		goto fail_put_clk;
>  	}
> -	info->drcmr_dat = r->start;
> +	nand->drcmr_dat = r->start;
>  
>  	r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
>  	if (r == NULL) {
> @@ -1027,7 +1036,7 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>  		ret = -ENXIO;
>  		goto fail_put_clk;
>  	}
> -	info->drcmr_cmd = r->start;
> +	nand->drcmr_cmd = r->start;
>  
>  	irq = platform_get_irq(pdev, 0);
>  	if (irq < 0) {
> @@ -1050,81 +1059,147 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
>  		goto fail_put_clk;
>  	}
>  
> -	info->mmio_base = ioremap(r->start, resource_size(r));
> -	if (info->mmio_base == NULL) {
> +	nand->mmio_base = ioremap(r->start, resource_size(r));
> +	if (nand->mmio_base == NULL) {
>  		dev_err(&pdev->dev, "ioremap() failed\n");
>  		ret = -ENODEV;
>  		goto fail_free_res;
>  	}
> -	info->mmio_phys = r->start;
> -
> -	ret = pxa3xx_nand_init_buff(info);
> -	if (ret)
> -		goto fail_free_io;
> +	nand->mmio_phys = r->start;
>  
>  	/* initialize all interrupts to be disabled */
> -	disable_int(info, NDSR_MASK);
> +	disable_int(nand, NDSR_MASK);
>  
>  	ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED,
> -			  pdev->name, info);
> +			  pdev->name, nand);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "failed to request IRQ\n");
> -		goto fail_free_buf;
> +		ret = -ENXIO;
> +		goto fail_free_io;
> +	}
> +
> +	platform_set_drvdata(pdev, nand);
> +
> +	spin_lock_init(&nand->controller.lock);
> +	init_waitqueue_head(&nand->controller.wq);
> +	for (cs = 0; cs < pdata->cs_num; cs++) {
> +		mtd = kzalloc(sizeof(struct mtd_info)
> +				+ sizeof(struct pxa3xx_nand_info),
> +				GFP_KERNEL);
> +		if (!mtd) {
> +			dev_err(&pdev->dev, "failed to allocate memory\n");
> +			ret = -ENOMEM;
> +			goto fail_free_irq;
> +		}
> +
> +		info = (struct pxa3xx_nand_info *)(&mtd[1]);
> +		info->nand_data = nand;
> +		info->chip_select = cs;
> +		mtd->priv = info;
> +		mtd->owner = THIS_MODULE;
> +		nand->info[cs] = info;
> +
> +		chip = (struct nand_chip *)(&mtd[1]);
> +		chip->controller        = &nand->controller;
> +		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
> +		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
> +		chip->waitfunc		= pxa3xx_nand_waitfunc;
> +		chip->select_chip	= pxa3xx_nand_select_chip;
> +		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
> +		chip->read_word		= pxa3xx_nand_read_word;
> +		chip->read_byte		= pxa3xx_nand_read_byte;
> +		chip->read_buf		= pxa3xx_nand_read_buf;
> +		chip->write_buf		= pxa3xx_nand_write_buf;
> +		chip->verify_buf	= pxa3xx_nand_verify_buf;
>  	}
>  
> -	platform_set_drvdata(pdev, info);
> +	if (use_dma == 0) {
> +		nand->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
> +		if (nand->data_buff == NULL) {
> +			ret = -ENOMEM;
> +			goto fail_free_buf;
> +		}
> +		goto success_exit;
> +	}
>  
> -	return info;
> +	nand->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
> +			&nand->data_buff_phys, GFP_KERNEL);
> +	if (nand->data_buff == NULL) {
> +		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
> +		ret = -ENOMEM;
> +		goto fail_free_buf;
> +	}
>  
> +	nand->data_desc = (void *)nand->data_buff + data_desc_offset;
> +	nand->data_desc_addr = nand->data_buff_phys + data_desc_offset;
> +	nand->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
> +			pxa3xx_nand_data_dma_irq, nand);
> +	if (nand->data_dma_ch < 0) {
> +		dev_err(&pdev->dev, "failed to request data dma\n");
> +		ret = -ENXIO;
> +		goto fail_free_dma_buf;
> +	}
> +success_exit:
> +	return 0;
> +
> +fail_free_dma_buf:
> +	dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
> +			nand->data_buff, nand->data_buff_phys);
>  fail_free_buf:
> -	free_irq(irq, info);
> -	if (use_dma) {
> -		pxa_free_dma(info->data_dma_ch);
> -		dma_free_coherent(&pdev->dev, info->data_buff_size,
> -			info->data_buff, info->data_buff_phys);
> -	} else
> -		kfree(info->data_buff);
> +	for (cs = 0; cs < pdata->cs_num; cs++) {
> +		info = nand->info[cs];
> +		free_cs_resource(info, cs);
> +	}
> +fail_free_irq:
> +	free_irq(irq, nand);
>  fail_free_io:
> -	iounmap(info->mmio_base);
> +	iounmap(nand->mmio_base);
>  fail_free_res:
>  	release_mem_region(r->start, resource_size(r));
>  fail_put_clk:
> -	clk_disable(info->clk);
> -	clk_put(info->clk);
> -fail_free_mtd:
> -	kfree(mtd);
> -	return NULL;
> +	clk_disable(nand->clk);
> +	clk_put(nand->clk);
> +fail_alloc:
> +	kfree(nand);
> +	return ret;
>  }
>  
>  static int pxa3xx_nand_remove(struct platform_device *pdev)
>  {
> -	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> -	struct mtd_info *mtd = info->mtd;
> +	struct pxa3xx_nand *nand = platform_get_drvdata(pdev);
> +	struct pxa3xx_nand_platform_data *pdata;
> +	struct pxa3xx_nand_info *info;
> +	struct mtd_info *mtd;
>  	struct resource *r;
> -	int irq;
> +	int irq, cs;
>  
>  	platform_set_drvdata(pdev, NULL);
> +	pdata = pdev->dev.platform_data;
>  
>  	irq = platform_get_irq(pdev, 0);
>  	if (irq >= 0)
> -		free_irq(irq, info);
> +		free_irq(irq, nand);
>  	if (use_dma) {
> -		pxa_free_dma(info->data_dma_ch);
> -		dma_free_writecombine(&pdev->dev, info->data_buff_size,
> -				info->data_buff, info->data_buff_phys);
> +		pxa_free_dma(nand->data_dma_ch);
> +		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
> +				nand->data_buff, nand->data_buff_phys);
>  	} else
> -		kfree(info->data_buff);
> +		kfree(nand->data_buff);
>  
> -	iounmap(info->mmio_base);
> +	iounmap(nand->mmio_base);
>  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	release_mem_region(r->start, resource_size(r));
>  
> -	clk_disable(info->clk);
> -	clk_put(info->clk);
> +	clk_disable(nand->clk);
> +	clk_put(nand->clk);
>  
> -	if (mtd) {
> +	for (cs = 0; cs < pdata->cs_num; cs++) {
> +		info = nand->info[cs];
> +		if (!info)
> +			continue;
> +		mtd = get_mtd_by_info(info);
>  		mtd_device_unregister(mtd);
> -		kfree(mtd);
> +		free_cs_resource(info, cs);
>  	}
>  	return 0;
>  }
> @@ -1134,34 +1209,54 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
>  	struct pxa3xx_nand_platform_data *pdata;
>  	struct pxa3xx_nand_info *info;
>  
> +	struct pxa3xx_nand *nand;
> +	struct mtd_info *mtd;
> +	int cs, ret, nr_parts, probe_success;
> +
> +	probe_success = 0;

Can this be done along with declaration?

>  	pdata = pdev->dev.platform_data;
>  	if (!pdata) {
>  		dev_err(&pdev->dev, "no platform data defined\n");
>  		return -ENODEV;
>  	}
>  
> -	info = alloc_nand_resource(pdev);
> -	if (info == NULL)
> +	ret = alloc_nand_resource(pdev);
> +	if (ret)
>  		return -ENOMEM;

Why not propagate the return value of alloc_nand_resource()?

>  
> -	if (pxa3xx_nand_scan(info->mtd)) {
> -		dev_err(&pdev->dev, "failed to scan nand\n");
> -		pxa3xx_nand_remove(pdev);
> -		return -ENODEV;
> -	}
> +	nand = platform_get_drvdata(pdev);
> +	for (cs = 0; cs < pdata->cs_num; cs++) {
> +		info = nand->info[cs];
> +		mtd = get_mtd_by_info(info);
> +		if (pxa3xx_nand_scan(mtd)) {
> +			dev_err(&pdev->dev, "failed to scan nand\n");

I think, it would be useful also to print here the return value of pxa3xx_nand_scan().

> +			continue;
> +		}
> +
> +		ret = 0;
> +		nr_parts = 0;
> +		if (mtd_has_cmdlinepart()) {
> +			const char *probes[] = { "cmdlinepart", NULL };
> +			struct mtd_partition *parts;
>  
> -	if (mtd_has_cmdlinepart()) {
> -		const char *probes[] = { "cmdlinepart", NULL };
> -		struct mtd_partition *parts;
> -		int nr_parts;
> +			nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0);
>  
> -		nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0);
> +			if (nr_parts)
> +				ret = mtd_device_register(mtd, parts, nr_parts);
> +		}
>  
> -		if (nr_parts)
> -			return mtd_device_register(info->mtd, parts, nr_parts);
> +		if (!nr_parts)
> +			ret = mtd_device_register(mtd, pdata->parts[cs],
> +					pdata->nr_parts[cs]);
> +		if (!ret)
> +			probe_success = 1;
>  	}
>  
> -	return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
> +	if (!probe_success) {
> +		pxa3xx_nand_remove(pdev);
> +		return -ENODEV;
> +	} else

You don't need this else statement

> +		return 0;
>  }
>  
>  #ifdef CONFIG_PM
> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>  	struct mtd_info *mtd = info->mtd;
>  
> -	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
> -	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
> +	nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
> +	nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>  	clk_enable(info->clk);
>  
>  	return 0;

I won't be able to test the patch in the near future, sorry...

-- 
Regards,
Igor.


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-08 17:38     ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-08 17:38 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Daniel,

On Wed, Jun 22, 2011 at 7:39 PM, Daniel Mack <zonque@gmail.com> wrote:
> On Wed, Jun 22, 2011 at 5:17 AM, Lei Wen <leiwen@marvell.com> wrote:
>> Current pxa3xx_nand controller has two chip select which
>> both be workable. This patch enable this feature.
>>
>> Update platform driver to support this feature.
>>
>> Another 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.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>> ?arch/arm/mach-mmp/aspenite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/cm-x300.c ? ? ? ? ? ? ? ? ?| ? ?5 +-
>> ?arch/arm/mach-pxa/colibri-pxa3xx.c ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/littleton.c ? ? ? ? ? ? ? ?| ? ?5 +-
>> ?arch/arm/mach-pxa/mxm8x10.c ? ? ? ? ? ? ? ? ?| ? ?9 +-
>> ?arch/arm/mach-pxa/raumfeld.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/mach-pxa/zylonite.c ? ? ? ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/plat-pxa/include/plat/pxa3xx_nand.h | ? ?8 +-
>> ?drivers/mtd/nand/pxa3xx_nand.c ? ? ? ? ? ? ? | ?735 +++++++++++++++-----------
>> ?9 files changed, 444 insertions(+), 338 deletions(-)
>
> This patch doesn't apply for me on top of Linus' master branch. Which
> tree are you based on currently?

Actually it is based on Linux 3.0-rc2 and add two patches pending to
l2-mtd-2.6.git already:

mtd: pxa3xx_nand: fix nand detection issue
mtd: pxa3xx_nand: Fix blank page ECC mismatch

>
> [...]
>
>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
>> index 30689cc..259b8d5 100644
>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>> +++ b/drivers/mtd/nand/pxa3xx_nand.c
>> @@ -92,11 +92,13 @@
>> ?#define NDCB0_ADDR_CYC_SHIFT ? (16)
>>
>> ?/* macros for registers read/write */
>> -#define nand_writel(info, off, val) ? ?\
>> - ? ? ? __raw_writel((val), (info)->mmio_base + (off))
>> +#define nand_writel(nand, off, val) ? ?\
>> + ? ? ? __raw_writel((val), (nand)->mmio_base + (off))
>>
>> -#define nand_readl(info, off) ? ? ? ? ?\
>> - ? ? ? __raw_readl((info)->mmio_base + (off))
>> +#define nand_readl(nand, off) ? ? ? ? ?\
>> + ? ? ? __raw_readl((nand)->mmio_base + (off))
>> +#define get_mtd_by_info(info) ? ? ? ? ?\
>> + ? ? ? (struct mtd_info *)((void *)info - sizeof(struct mtd_info))
>>
>> ?/* error code and state */
>> ?enum {
>> @@ -110,6 +112,7 @@ enum {
>>
>> ?enum {
>> ? ? ? ?STATE_IDLE = 0,
>> + ? ? ? STATE_PREPARED,
>> ? ? ? ?STATE_CMD_HANDLE,
>> ? ? ? ?STATE_DMA_READING,
>> ? ? ? ?STATE_DMA_WRITING,
>> @@ -123,63 +126,63 @@ enum {
>> ?struct pxa3xx_nand_info {
>> ? ? ? ?struct nand_chip ? ? ? ?nand_chip;
>>
>> - ? ? ? struct nand_hw_control ?controller;
>> - ? ? ? struct platform_device ? *pdev;
>> ? ? ? ?struct pxa3xx_nand_cmdset *cmdset;
>> + ? ? ? /* page size of attached chip */
>> + ? ? ? uint16_t ? ? ? ? ? ? ? ?page_size;
>> + ? ? ? uint8_t ? ? ? ? ? ? ? ? chip_select;
>> + ? ? ? uint8_t ? ? ? ? ? ? ? ? use_ecc;
>> +
>> + ? ? ? /* calculated from pxa3xx_nand_flash data */
>> + ? ? ? uint8_t ? ? ? ? ? ? ? ? col_addr_cycles;
>> + ? ? ? uint8_t ? ? ? ? ? ? ? ? row_addr_cycles;
>> + ? ? ? uint8_t ? ? ? ? ? ? ? ? read_id_bytes;
>> +
>> + ? ? ? /* cached register value */
>> + ? ? ? uint32_t ? ? ? ? ? ? ? ?reg_ndcr;
>> + ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr0cs0;
>> + ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr1cs0;
>>
>> + ? ? ? void ? ? ? ? ? ? ? ? ? ?*nand_data;
>> +};
>> +
>> +struct pxa3xx_nand {
>> ? ? ? ?struct clk ? ? ? ? ? ? ?*clk;
>> ? ? ? ?void __iomem ? ? ? ? ? ?*mmio_base;
>> ? ? ? ?unsigned long ? ? ? ? ? mmio_phys;
>> + ? ? ? struct nand_hw_control ?controller;
>> + ? ? ? struct completion ? ? ? cmd_complete;
>> + ? ? ? struct platform_device ? *pdev;
>>
>> - ? ? ? unsigned int ? ? ? ? ? ?buf_start;
>> - ? ? ? unsigned int ? ? ? ? ? ?buf_count;
>> -
>> - ? ? ? struct mtd_info ? ? ? ? *mtd;
>> ? ? ? ?/* DMA information */
>> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? drcmr_dat;
>> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? drcmr_cmd;
>> -
>> - ? ? ? unsigned char ? ? ? ? ? *data_buff;
>> - ? ? ? unsigned char ? ? ? ? ? *oob_buff;
>> - ? ? ? dma_addr_t ? ? ? ? ? ? ?data_buff_phys;
>> - ? ? ? size_t ? ? ? ? ? ? ? ? ?data_buff_size;
>> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? data_dma_ch;
>> - ? ? ? struct pxa_dma_desc ? ? *data_desc;
>> + ? ? ? dma_addr_t ? ? ? ? ? ? ?data_buff_phys;
>> ? ? ? ?dma_addr_t ? ? ? ? ? ? ?data_desc_addr;
>> + ? ? ? struct pxa_dma_desc ? ? *data_desc;
>>
>> - ? ? ? uint32_t ? ? ? ? ? ? ? ?reg_ndcr;
>> -
>> - ? ? ? /* saved column/page_addr during CMD_SEQIN */
>> - ? ? ? int ? ? ? ? ? ? ? ? ? ? seqin_column;
>> - ? ? ? int ? ? ? ? ? ? ? ? ? ? seqin_page_addr;
>> + ? ? ? struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
>> + ? ? ? uint32_t ? ? ? ? ? ? ? ?command;
>> + ? ? ? uint16_t ? ? ? ? ? ? ? ?data_size; ? ? ?/* data size in FIFO */
>> + ? ? ? uint16_t ? ? ? ? ? ? ? ?oob_size;
>> + ? ? ? unsigned char ? ? ? ? ? *data_buff;
>> + ? ? ? unsigned char ? ? ? ? ? *oob_buff;
>> + ? ? ? uint32_t ? ? ? ? ? ? ? ?buf_start;
>> + ? ? ? uint32_t ? ? ? ? ? ? ? ?buf_count;
>>
>> ? ? ? ?/* relate to the command */
>> ? ? ? ?unsigned int ? ? ? ? ? ?state;
>> -
>> + ? ? ? uint8_t ? ? ? ? ? ? ? ? chip_select;
>> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? use_ecc; ? ? ? ?/* use HW ECC ? */
>> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? use_dma; ? ? ? ?/* use DMA ? */
>> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? is_ready;
>> -
>> - ? ? ? unsigned int ? ? ? ? ? ?page_size; ? ? ?/* page size of attached chip */
>> - ? ? ? unsigned int ? ? ? ? ? ?data_size; ? ? ?/* data size in FIFO */
>> ? ? ? ?int ? ? ? ? ? ? ? ? ? ? retcode;
>> - ? ? ? struct completion ? ? ? cmd_complete;
>>
>> ? ? ? ?/* generated NDCBx register values */
>> + ? ? ? uint8_t ? ? ? ? ? ? ? ? total_cmds;
>> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb0;
>> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb1;
>> ? ? ? ?uint32_t ? ? ? ? ? ? ? ?ndcb2;
>> -
>> - ? ? ? /* timing calcuted from setting */
>> - ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr0cs0;
>> - ? ? ? uint32_t ? ? ? ? ? ? ? ?ndtr1cs0;
>> -
>> - ? ? ? /* calculated from pxa3xx_nand_flash data */
>> - ? ? ? size_t ? ? ? ? ?oob_size;
>> - ? ? ? size_t ? ? ? ? ?read_id_bytes;
>> -
>> - ? ? ? unsigned int ? ?col_addr_cycles;
>> - ? ? ? unsigned int ? ?row_addr_cycles;
>> ?};
>>
>> ?static int use_dma = 1;
>> @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>> ?/* Define a default flash type setting serve as flash detecting only */
>> ?#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>>
>> -const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>> +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
>>
>> ?#define NDTR0_tCH(c) ? (min((c), 7) << 19)
>> ?#define NDTR0_tCS(c) ? (min((c), 7) << 16)
>> @@ -244,9 +247,11 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
>> ?static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const struct pxa3xx_nand_timing *t)
>> ?{
>> - ? ? ? unsigned long nand_clk = clk_get_rate(info->clk);
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? ? unsigned long nand_clk;
>> ? ? ? ?uint32_t ndtr0, ndtr1;
>>
>> + ? ? ? nand_clk = clk_get_rate(nand->clk);
>> ? ? ? ?ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
>> ? ? ? ? ? ? ? ?NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
>> ? ? ? ? ? ? ? ?NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
>> @@ -260,26 +265,27 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
>>
>> ? ? ? ?info->ndtr0cs0 = ndtr0;
>> ? ? ? ?info->ndtr1cs0 = ndtr1;
>> - ? ? ? nand_writel(info, NDTR0CS0, ndtr0);
>> - ? ? ? nand_writel(info, NDTR1CS0, ndtr1);
>> + ? ? ? nand_writel(nand, NDTR0CS0, ndtr0);
>> + ? ? ? nand_writel(nand, NDTR1CS0, ndtr1);
>> ?}
>>
>> ?static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>> ?{
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> ? ? ? ?int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
>>
>> - ? ? ? info->data_size = info->page_size;
>> + ? ? ? nand->data_size = info->page_size;
>> ? ? ? ?if (!oob_enable) {
>> - ? ? ? ? ? ? ? info->oob_size = 0;
>> + ? ? ? ? ? ? ? nand->oob_size = 0;
>> ? ? ? ? ? ? ? ?return;
>> ? ? ? ?}
>>
>> ? ? ? ?switch (info->page_size) {
>> ? ? ? ?case 2048:
>> - ? ? ? ? ? ? ? info->oob_size = (info->use_ecc) ? 40 : 64;
>> + ? ? ? ? ? ? ? nand->oob_size = (info->use_ecc) ? 40 : 64;
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?case 512:
>> - ? ? ? ? ? ? ? info->oob_size = (info->use_ecc) ? 8 : 16;
>> + ? ? ? ? ? ? ? nand->oob_size = (info->use_ecc) ? 8 : 16;
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?}
>> ?}
>> @@ -290,185 +296,189 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
>> ?* 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_info *info)
>> +static void pxa3xx_nand_start(struct pxa3xx_nand *nand)
>> ?{
>> + ? ? ? struct pxa3xx_nand_info *info;
>> ? ? ? ?uint32_t ndcr;
>>
>> + ? ? ? info = nand->info[nand->chip_select];
>> ? ? ? ?ndcr = info->reg_ndcr;
>> - ? ? ? ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
>> - ? ? ? ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
>> + ? ? ? ndcr |= nand->use_ecc ? NDCR_ECC_EN : 0;
>> + ? ? ? ndcr |= nand->use_dma ? NDCR_DMA_EN : 0;
>> ? ? ? ?ndcr |= NDCR_ND_RUN;
>>
>> ? ? ? ?/* clear status bits and run */
>> - ? ? ? nand_writel(info, NDCR, 0);
>> - ? ? ? nand_writel(info, NDSR, NDSR_MASK);
>> - ? ? ? nand_writel(info, NDCR, ndcr);
>> + ? ? ? nand_writel(nand, NDCR, 0);
>> + ? ? ? nand_writel(nand, NDSR, NDSR_MASK);
>> + ? ? ? nand_writel(nand, NDCR, ndcr);
>> ?}
>>
>> -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
>> +static void pxa3xx_nand_stop(struct pxa3xx_nand *nand)
>> ?{
>> ? ? ? ?uint32_t ndcr;
>> ? ? ? ?int timeout = NAND_STOP_DELAY;
>>
>> ? ? ? ?/* wait RUN bit in NDCR become 0 */
>> - ? ? ? ndcr = nand_readl(info, NDCR);
>> + ? ? ? ndcr = nand_readl(nand, NDCR);
>> ? ? ? ?while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
>> - ? ? ? ? ? ? ? ndcr = nand_readl(info, NDCR);
>> + ? ? ? ? ? ? ? ndcr = nand_readl(nand, NDCR);
>> ? ? ? ? ? ? ? ?udelay(1);
>> ? ? ? ?}
>>
>> ? ? ? ?if (timeout <= 0) {
>> ? ? ? ? ? ? ? ?ndcr &= ~NDCR_ND_RUN;
>> - ? ? ? ? ? ? ? nand_writel(info, NDCR, ndcr);
>> + ? ? ? ? ? ? ? nand_writel(nand, NDCR, ndcr);
>> ? ? ? ?}
>> ? ? ? ?/* clear status bits */
>> - ? ? ? nand_writel(info, NDSR, NDSR_MASK);
>> + ? ? ? nand_writel(nand, NDSR, NDSR_MASK);
>> ?}
>>
>> -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>> +static void enable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>> ?{
>> ? ? ? ?uint32_t ndcr;
>>
>> - ? ? ? ndcr = nand_readl(info, NDCR);
>> - ? ? ? nand_writel(info, NDCR, ndcr & ~int_mask);
>> + ? ? ? ndcr = nand_readl(nand, NDCR);
>> + ? ? ? nand_writel(nand, NDCR, ndcr & ~int_mask);
>> ?}
>>
>> -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>> +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask)
>> ?{
>> ? ? ? ?uint32_t ndcr;
>>
>> - ? ? ? ndcr = nand_readl(info, NDCR);
>> - ? ? ? nand_writel(info, NDCR, ndcr | int_mask);
>> + ? ? ? ndcr = nand_readl(nand, NDCR);
>> + ? ? ? nand_writel(nand, NDCR, ndcr | int_mask);
>> ?}
>>
>> -static void handle_data_pio(struct pxa3xx_nand_info *info)
>> +static void handle_data_pio(struct pxa3xx_nand *nand)
>> ?{
>> - ? ? ? switch (info->state) {
>> + ? ? ? switch (nand->state) {
>> ? ? ? ?case STATE_PIO_WRITING:
>> - ? ? ? ? ? ? ? __raw_writesl(info->mmio_base + NDDB, info->data_buff,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->data_size, 4));
>> - ? ? ? ? ? ? ? if (info->oob_size > 0)
>> - ? ? ? ? ? ? ? ? ? ? ? __raw_writesl(info->mmio_base + NDDB, info->oob_buff,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->oob_size, 4));
>> + ? ? ? ? ? ? ? __raw_writesl(nand->mmio_base + NDDB, nand->data_buff,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->data_size, 4));
>> + ? ? ? ? ? ? ? if (nand->oob_size > 0)
>> + ? ? ? ? ? ? ? ? ? ? ? __raw_writesl(nand->mmio_base + NDDB, nand->oob_buff,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->oob_size, 4));
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?case STATE_PIO_READING:
>> - ? ? ? ? ? ? ? __raw_readsl(info->mmio_base + NDDB, info->data_buff,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->data_size, 4));
>> - ? ? ? ? ? ? ? if (info->oob_size > 0)
>> - ? ? ? ? ? ? ? ? ? ? ? __raw_readsl(info->mmio_base + NDDB, info->oob_buff,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(info->oob_size, 4));
>> + ? ? ? ? ? ? ? __raw_readsl(nand->mmio_base + NDDB, nand->data_buff,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->data_size, 4));
>> + ? ? ? ? ? ? ? if (nand->oob_size > 0)
>> + ? ? ? ? ? ? ? ? ? ? ? __raw_readsl(nand->mmio_base + NDDB, nand->oob_buff,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DIV_ROUND_UP(nand->oob_size, 4));
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?default:
>> ? ? ? ? ? ? ? ?printk(KERN_ERR "%s: invalid state %d\n", __func__,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->state);
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->state);
>
> Can't you use dev_err() here?

Good suggestion, I would make this change.
>
>> ? ? ? ? ? ? ? ?BUG();
>
> Is crashing the entire kernel really necessary here?
>
>> ? ? ? ?}
>> ?}
>>
>> -static void start_data_dma(struct pxa3xx_nand_info *info)
>> +static void start_data_dma(struct pxa3xx_nand *nand)
>> ?{
>> - ? ? ? struct pxa_dma_desc *desc = info->data_desc;
>> - ? ? ? int dma_len = ALIGN(info->data_size + info->oob_size, 32);
>> + ? ? ? struct pxa_dma_desc *desc = nand->data_desc;
>> + ? ? ? int dma_len = ALIGN(nand->data_size + nand->oob_size, 32);
>>
>> ? ? ? ?desc->ddadr = DDADR_STOP;
>> ? ? ? ?desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
>>
>> - ? ? ? switch (info->state) {
>> + ? ? ? switch (nand->state) {
>> ? ? ? ?case STATE_DMA_WRITING:
>> - ? ? ? ? ? ? ? desc->dsadr = info->data_buff_phys;
>> - ? ? ? ? ? ? ? desc->dtadr = info->mmio_phys + NDDB;
>> + ? ? ? ? ? ? ? desc->dsadr = nand->data_buff_phys;
>> + ? ? ? ? ? ? ? desc->dtadr = nand->mmio_phys + NDDB;
>> ? ? ? ? ? ? ? ?desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?case STATE_DMA_READING:
>> - ? ? ? ? ? ? ? desc->dtadr = info->data_buff_phys;
>> - ? ? ? ? ? ? ? desc->dsadr = info->mmio_phys + NDDB;
>> + ? ? ? ? ? ? ? desc->dtadr = nand->data_buff_phys;
>> + ? ? ? ? ? ? ? desc->dsadr = nand->mmio_phys + NDDB;
>> ? ? ? ? ? ? ? ?desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?default:
>> ? ? ? ? ? ? ? ?printk(KERN_ERR "%s: invalid state %d\n", __func__,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->state);
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->state);
>> ? ? ? ? ? ? ? ?BUG();
>> ? ? ? ?}
>>
>> - ? ? ? DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
>> - ? ? ? DDADR(info->data_dma_ch) = info->data_desc_addr;
>> - ? ? ? DCSR(info->data_dma_ch) |= DCSR_RUN;
>> + ? ? ? DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
>> + ? ? ? DDADR(nand->data_dma_ch) = nand->data_desc_addr;
>> + ? ? ? DCSR(nand->data_dma_ch) |= DCSR_RUN;
>> ?}
>>
>> ?static void pxa3xx_nand_data_dma_irq(int channel, void *data)
>> ?{
>> - ? ? ? struct pxa3xx_nand_info *info = data;
>> + ? ? ? struct pxa3xx_nand *nand = data;
>> ? ? ? ?uint32_t dcsr;
>>
>> ? ? ? ?dcsr = DCSR(channel);
>> ? ? ? ?DCSR(channel) = dcsr;
>>
>> ? ? ? ?if (dcsr & DCSR_BUSERR) {
>> - ? ? ? ? ? ? ? info->retcode = ERR_DMABUSERR;
>> + ? ? ? ? ? ? ? nand->retcode = ERR_DMABUSERR;
>> ? ? ? ?}
>>
>> - ? ? ? info->state = STATE_DMA_DONE;
>> - ? ? ? enable_int(info, NDCR_INT_MASK);
>> - ? ? ? nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>> + ? ? ? nand->state = STATE_DMA_DONE;
>> + ? ? ? enable_int(nand, NDCR_INT_MASK);
>> + ? ? ? nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
>> ?}
>>
>> ?static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>> ?{
>> - ? ? ? struct pxa3xx_nand_info *info = devid;
>> - ? ? ? unsigned int status, is_completed = 0;
>> + ? ? ? struct pxa3xx_nand *nand = devid;
>> + ? ? ? struct pxa3xx_nand_info *info;
>> + ? ? ? unsigned int status, is_completed = 0, cs;
>> + ? ? ? unsigned int ready, cmd_done, page_done, badblock_detect;
>>
>> - ? ? ? status = nand_readl(info, NDSR);
>> + ? ? ? cs ? ? ? ? ? ? ?= nand->chip_select;
>> + ? ? ? ready ? ? ? ? ? = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
>> + ? ? ? cmd_done ? ? ? ?= (cs) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD;
>> + ? ? ? page_done ? ? ? = (cs) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED;
>> + ? ? ? badblock_detect = (cs) ? NDSR_CS1_BBD : NDSR_CS0_BBD;
>> + ? ? ? info ? ? ? ? ? ?= nand->info[cs];
>>
>> + ? ? ? status = nand_readl(nand, NDSR);
>> ? ? ? ?if (status & NDSR_DBERR)
>> - ? ? ? ? ? ? ? info->retcode = ERR_DBERR;
>> + ? ? ? ? ? ? ? nand->retcode = ERR_DBERR;
>> ? ? ? ?if (status & NDSR_SBERR)
>> - ? ? ? ? ? ? ? info->retcode = ERR_SBERR;
>> + ? ? ? ? ? ? ? nand->retcode = ERR_SBERR;
>> ? ? ? ?if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
>> ? ? ? ? ? ? ? ?/* whether use dma to transfer data */
>> - ? ? ? ? ? ? ? if (info->use_dma) {
>> - ? ? ? ? ? ? ? ? ? ? ? disable_int(info, NDCR_INT_MASK);
>> - ? ? ? ? ? ? ? ? ? ? ? info->state = (status & NDSR_RDDREQ) ?
>> + ? ? ? ? ? ? ? if (nand->use_dma) {
>> + ? ? ? ? ? ? ? ? ? ? ? disable_int(nand, NDCR_INT_MASK);
>> + ? ? ? ? ? ? ? ? ? ? ? nand->state = (status & NDSR_RDDREQ) ?
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?STATE_DMA_READING : STATE_DMA_WRITING;
>> - ? ? ? ? ? ? ? ? ? ? ? start_data_dma(info);
>> + ? ? ? ? ? ? ? ? ? ? ? start_data_dma(nand);
>> ? ? ? ? ? ? ? ? ? ? ? ?goto NORMAL_IRQ_EXIT;
>> ? ? ? ? ? ? ? ?} else {
>> - ? ? ? ? ? ? ? ? ? ? ? info->state = (status & NDSR_RDDREQ) ?
>> + ? ? ? ? ? ? ? ? ? ? ? nand->state = (status & NDSR_RDDREQ) ?
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?STATE_PIO_READING : STATE_PIO_WRITING;
>> - ? ? ? ? ? ? ? ? ? ? ? handle_data_pio(info);
>> + ? ? ? ? ? ? ? ? ? ? ? handle_data_pio(nand);
>> ? ? ? ? ? ? ? ?}
>> ? ? ? ?}
>> - ? ? ? if (status & NDSR_CS0_CMDD) {
>> - ? ? ? ? ? ? ? info->state = STATE_CMD_DONE;
>> + ? ? ? if (status & cmd_done) {
>> + ? ? ? ? ? ? ? nand->state = STATE_CMD_DONE;
>> ? ? ? ? ? ? ? ?is_completed = 1;
>> ? ? ? ?}
>> - ? ? ? if (status & NDSR_FLASH_RDY) {
>> - ? ? ? ? ? ? ? info->is_ready = 1;
>> - ? ? ? ? ? ? ? info->state = STATE_READY;
>> + ? ? ? if (status & ready) {
>> + ? ? ? ? ? ? ? nand->is_ready = 1;
>> + ? ? ? ? ? ? ? nand->state = STATE_READY;
>> ? ? ? ?}
>>
>> ? ? ? ?if (status & NDSR_WRCMDREQ) {
>> - ? ? ? ? ? ? ? nand_writel(info, NDSR, NDSR_WRCMDREQ);
>> + ? ? ? ? ? ? ? nand_writel(nand, NDSR, NDSR_WRCMDREQ);
>> ? ? ? ? ? ? ? ?status &= ~NDSR_WRCMDREQ;
>> - ? ? ? ? ? ? ? info->state = STATE_CMD_HANDLE;
>> - ? ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb0);
>> - ? ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb1);
>> - ? ? ? ? ? ? ? nand_writel(info, NDCB0, info->ndcb2);
>> + ? ? ? ? ? ? ? nand->state = STATE_CMD_HANDLE;
>> + ? ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb0);
>> + ? ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb1);
>> + ? ? ? ? ? ? ? nand_writel(nand, NDCB0, nand->ndcb2);
>> ? ? ? ?}
>>
>> ? ? ? ?/* clear NDSR to let the controller exit the IRQ */
>> - ? ? ? nand_writel(info, NDSR, status);
>> + ? ? ? nand_writel(nand, NDSR, status);
>> ? ? ? ?if (is_completed)
>> - ? ? ? ? ? ? ? complete(&info->cmd_complete);
>> + ? ? ? ? ? ? ? complete(&nand->cmd_complete);
>> ?NORMAL_IRQ_EXIT:
>> ? ? ? ?return IRQ_HANDLED;
>> ?}
>>
>> -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
>> -{
>> - ? ? ? struct pxa3xx_nand_info *info = mtd->priv;
>> - ? ? ? return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
>> -}
>> -
>> ?static inline int is_buf_blank(uint8_t *buf, size_t len)
>> ?{
>> ? ? ? ?for (; len > 0; len--)
>> @@ -477,42 +487,49 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
>> ? ? ? ?return 1;
>> ?}
>>
>> -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>> +static int prepare_command_pool(struct pxa3xx_nand *nand, int command,
>> ? ? ? ? ? ? ? ?uint16_t column, int page_addr)
>> ?{
>> ? ? ? ?uint16_t cmd;
>> ? ? ? ?int addr_cycle, exec_cmd, ndcb0;
>> - ? ? ? struct mtd_info *mtd = info->mtd;
>> + ? ? ? struct mtd_info *mtd;
>> + ? ? ? struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
>>
>> - ? ? ? ndcb0 = 0;
>> + ? ? ? mtd = get_mtd_by_info(info);
>> + ? ? ? ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;
>> ? ? ? ?addr_cycle = 0;
>> ? ? ? ?exec_cmd = 1;
>>
>> ? ? ? ?/* reset data and oob column point to handle data */
>> - ? ? ? info->buf_start ? ? ? ? = 0;
>> - ? ? ? info->buf_count ? ? ? ? = 0;
>> - ? ? ? info->oob_size ? ? ? ? ?= 0;
>> - ? ? ? info->use_ecc ? ? ? ? ? = 0;
>> - ? ? ? info->is_ready ? ? ? ? ?= 0;
>> - ? ? ? info->retcode ? ? ? ? ? = ERR_NONE;
>> + ? ? ? nand->buf_start ? ? ? ? = 0;
>> + ? ? ? nand->buf_count ? ? ? ? = 0;
>> + ? ? ? nand->oob_size ? ? ? ? ?= 0;
>> + ? ? ? nand->use_ecc ? ? ? ? ? = 0;
>> + ? ? ? nand->is_ready ? ? ? ? ?= 0;
>> + ? ? ? nand->retcode ? ? ? ? ? = ERR_NONE;
>> + ? ? ? nand->data_size ? ? ? ? = 0;
>> + ? ? ? nand->use_dma ? ? ? ? ? = 0;
>> + ? ? ? nand->command ? ? ? ? ? = command;
>>
>> ? ? ? ?switch (command) {
>> ? ? ? ?case NAND_CMD_READ0:
>> ? ? ? ?case NAND_CMD_PAGEPROG:
>> - ? ? ? ? ? ? ? info->use_ecc = 1;
>> + ? ? ? ? ? ? ? nand->use_ecc = 1;
>> ? ? ? ?case NAND_CMD_READOOB:
>> ? ? ? ? ? ? ? ?pxa3xx_set_datasize(info);
>> + ? ? ? ? ? ? ? nand->oob_buff = nand->data_buff + nand->data_size;
>> + ? ? ? ? ? ? ? nand->use_dma = use_dma;
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?case NAND_CMD_SEQIN:
>> ? ? ? ? ? ? ? ?exec_cmd = 0;
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?default:
>> - ? ? ? ? ? ? ? info->ndcb1 = 0;
>> - ? ? ? ? ? ? ? info->ndcb2 = 0;
>> + ? ? ? ? ? ? ? nand->ndcb1 = 0;
>> + ? ? ? ? ? ? ? nand->ndcb2 = 0;
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?}
>>
>> - ? ? ? info->ndcb0 = ndcb0;
>> + ? ? ? nand->ndcb0 = ndcb0;
>> ? ? ? ?addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?+ info->col_addr_cycles);
>>
>> @@ -521,16 +538,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>> ? ? ? ?case NAND_CMD_READ0:
>> ? ? ? ? ? ? ? ?cmd = info->cmdset->read1;
>> ? ? ? ? ? ? ? ?if (command == NAND_CMD_READOOB)
>> - ? ? ? ? ? ? ? ? ? ? ? info->buf_start = mtd->writesize + column;
>> + ? ? ? ? ? ? ? ? ? ? ? nand->buf_start = mtd->writesize + column;
>> ? ? ? ? ? ? ? ?else
>> - ? ? ? ? ? ? ? ? ? ? ? info->buf_start = column;
>> + ? ? ? ? ? ? ? ? ? ? ? nand->buf_start = column;
>>
>> ? ? ? ? ? ? ? ?if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
>> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0)
>> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| addr_cycle
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| (cmd & NDCB0_CMD1_MASK);
>> ? ? ? ? ? ? ? ?else
>> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0)
>> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_DBC
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| addr_cycle
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>> @@ -538,34 +555,34 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>> ? ? ? ?case NAND_CMD_SEQIN:
>> ? ? ? ? ? ? ? ?/* small page addr setting */
>> ? ? ? ? ? ? ? ?if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
>> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| (column & 0xFF);
>>
>> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb2 = 0;
>> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = 0;
>> ? ? ? ? ? ? ? ?} else {
>> - ? ? ? ? ? ? ? ? ? ? ? info->ndcb1 = ((page_addr & 0xFFFF) << 16)
>> + ? ? ? ? ? ? ? ? ? ? ? nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| (column & 0xFFFF);
>>
>> ? ? ? ? ? ? ? ? ? ? ? ?if (page_addr & 0xFF0000)
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->ndcb2 = (page_addr & 0xFF0000) >> 16;
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
>> ? ? ? ? ? ? ? ? ? ? ? ?else
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->ndcb2 = 0;
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->ndcb2 = 0;
>> ? ? ? ? ? ? ? ?}
>>
>> - ? ? ? ? ? ? ? info->buf_count = mtd->writesize + mtd->oobsize;
>> - ? ? ? ? ? ? ? memset(info->data_buff, 0xFF, info->buf_count);
>> + ? ? ? ? ? ? ? nand->buf_count = mtd->writesize + mtd->oobsize;
>> + ? ? ? ? ? ? ? memset(nand->data_buff, 0xFF, nand->buf_count);
>>
>> ? ? ? ? ? ? ? ?break;
>>
>> ? ? ? ?case NAND_CMD_PAGEPROG:
>> - ? ? ? ? ? ? ? if (is_buf_blank(info->data_buff,
>> + ? ? ? ? ? ? ? if (is_buf_blank(nand->data_buff,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(mtd->writesize + mtd->oobsize))) {
>> ? ? ? ? ? ? ? ? ? ? ? ?exec_cmd = 0;
>> ? ? ? ? ? ? ? ? ? ? ? ?break;
>> ? ? ? ? ? ? ? ?}
>>
>> ? ? ? ? ? ? ? ?cmd = info->cmdset->program;
>> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(0x1)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_AUTO_RS
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ST_ROW_EN
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_DBC
>> @@ -575,37 +592,37 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
>>
>> ? ? ? ?case NAND_CMD_READID:
>> ? ? ? ? ? ? ? ?cmd = info->cmdset->read_id;
>> - ? ? ? ? ? ? ? info->buf_count = info->read_id_bytes;
>> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(3)
>> + ? ? ? ? ? ? ? nand->buf_count = info->read_id_bytes;
>> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(3)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(1)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>>
>> - ? ? ? ? ? ? ? info->data_size = 8;
>> + ? ? ? ? ? ? ? nand->data_size = 8;
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?case NAND_CMD_STATUS:
>> ? ? ? ? ? ? ? ?cmd = info->cmdset->read_status;
>> - ? ? ? ? ? ? ? info->buf_count = 1;
>> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(4)
>> + ? ? ? ? ? ? ? nand->buf_count = 1;
>> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(4)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(1)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>>
>> - ? ? ? ? ? ? ? info->data_size = 8;
>> + ? ? ? ? ? ? ? nand->data_size = 8;
>> ? ? ? ? ? ? ? ?break;
>>
>> ? ? ? ?case NAND_CMD_ERASE1:
>> ? ? ? ? ? ? ? ?cmd = info->cmdset->erase;
>> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(2)
>> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(2)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_AUTO_RS
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_ADDR_CYC(3)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| NDCB0_DBC
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>> - ? ? ? ? ? ? ? info->ndcb1 = page_addr;
>> - ? ? ? ? ? ? ? info->ndcb2 = 0;
>> + ? ? ? ? ? ? ? nand->ndcb1 = page_addr;
>> + ? ? ? ? ? ? ? nand->ndcb2 = 0;
>>
>> ? ? ? ? ? ? ? ?break;
>> ? ? ? ?case NAND_CMD_RESET:
>> ? ? ? ? ? ? ? ?cmd = info->cmdset->reset;
>> - ? ? ? ? ? ? ? info->ndcb0 |= NDCB0_CMD_TYPE(5)
>> + ? ? ? ? ? ? ? nand->ndcb0 |= NDCB0_CMD_TYPE(5)
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| cmd;
>>
>> ? ? ? ? ? ? ? ?break;
>> @@ -628,6 +645,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int column, int page_addr)
>> ?{
>> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> ? ? ? ?int ret, exec_cmd;
>>
>> ? ? ? ?/*
>> @@ -638,20 +656,32 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
>> ? ? ? ?if (info->reg_ndcr & NDCR_DWIDTH_M)
>> ? ? ? ? ? ? ? ?column /= 2;
>>
>> - ? ? ? exec_cmd = prepare_command_pool(info, command, column, page_addr);
>> + ? ? ? /*
>> + ? ? ? ?* There may be different NAND chip hooked to
>> + ? ? ? ?* different chip select, so check whether
>> + ? ? ? ?* chip select has been changed, if yes, reset the timing
>> + ? ? ? ?*/
>> + ? ? ? if (nand->chip_select != info->chip_select) {
>> + ? ? ? ? ? ? ? nand->chip_select = info->chip_select;
>> + ? ? ? ? ? ? ? nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>> + ? ? ? ? ? ? ? nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>> + ? ? ? }
>> +
>> + ? ? ? nand->state = STATE_PREPARED;
>> + ? ? ? exec_cmd = prepare_command_pool(nand, command, column, page_addr);
>> ? ? ? ?if (exec_cmd) {
>> - ? ? ? ? ? ? ? init_completion(&info->cmd_complete);
>> - ? ? ? ? ? ? ? pxa3xx_nand_start(info);
>> + ? ? ? ? ? ? ? init_completion(&nand->cmd_complete);
>> + ? ? ? ? ? ? ? pxa3xx_nand_start(nand);
>>
>> - ? ? ? ? ? ? ? ret = wait_for_completion_timeout(&info->cmd_complete,
>> + ? ? ? ? ? ? ? ret = wait_for_completion_timeout(&nand->cmd_complete,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CHIP_DELAY_TIMEOUT);
>> ? ? ? ? ? ? ? ?if (!ret) {
>> ? ? ? ? ? ? ? ? ? ? ? ?printk(KERN_ERR "Wait time out!!!\n");
>> ? ? ? ? ? ? ? ? ? ? ? ?/* Stop State Machine for next command cycle */
>> - ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_stop(info);
>> + ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_stop(nand);
>> ? ? ? ? ? ? ? ?}
>> - ? ? ? ? ? ? ? info->state = STATE_IDLE;
>> ? ? ? ?}
>> + ? ? ? nand->state = STATE_IDLE;
>> ?}
>>
>> ?static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
>> @@ -665,11 +695,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>> ? ? ? ? ? ? ? ?struct nand_chip *chip, uint8_t *buf, int page)
>> ?{
>> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>>
>> ? ? ? ?chip->read_buf(mtd, buf, mtd->writesize);
>> ? ? ? ?chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
>>
>> - ? ? ? if (info->retcode == ERR_SBERR) {
>> + ? ? ? if (nand->retcode == ERR_SBERR) {
>> ? ? ? ? ? ? ? ?switch (info->use_ecc) {
>> ? ? ? ? ? ? ? ?case 1:
>> ? ? ? ? ? ? ? ? ? ? ? ?mtd->ecc_stats.corrected++;
>> @@ -678,14 +709,14 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>> ? ? ? ? ? ? ? ?default:
>> ? ? ? ? ? ? ? ? ? ? ? ?break;
>> ? ? ? ? ? ? ? ?}
>> - ? ? ? } else if (info->retcode == ERR_DBERR) {
>> + ? ? ? } else if (nand->retcode == ERR_DBERR) {
>> ? ? ? ? ? ? ? ?/*
>> ? ? ? ? ? ? ? ? * for blank page (all 0xff), HW will calculate its ECC as
>> ? ? ? ? ? ? ? ? * 0, which is different from the ECC information within
>> ? ? ? ? ? ? ? ? * OOB, ignore such double bit errors
>> ? ? ? ? ? ? ? ? */
>> ? ? ? ? ? ? ? ?if (is_buf_blank(buf, mtd->writesize))
>> - ? ? ? ? ? ? ? ? ? ? ? info->retcode = ERR_NONE;
>> + ? ? ? ? ? ? ? ? ? ? ? nand->retcode = ERR_NONE;
>> ? ? ? ? ? ? ? ?else
>> ? ? ? ? ? ? ? ? ? ? ? ?mtd->ecc_stats.failed++;
>> ? ? ? ?}
>> @@ -696,11 +727,12 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
>> ?static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>> ?{
>> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> ? ? ? ?char retval = 0xFF;
>>
>> - ? ? ? if (info->buf_start < info->buf_count)
>> + ? ? ? if (nand->buf_start < nand->buf_count)
>> ? ? ? ? ? ? ? ?/* Has just send a new command? */
>> - ? ? ? ? ? ? ? retval = info->data_buff[info->buf_start++];
>> + ? ? ? ? ? ? ? retval = nand->data_buff[nand->buf_start++];
>>
>> ? ? ? ?return retval;
>> ?}
>> @@ -708,11 +740,12 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
>> ?static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>> ?{
>> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> ? ? ? ?u16 retval = 0xFFFF;
>>
>> - ? ? ? if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
>> - ? ? ? ? ? ? ? retval = *((u16 *)(info->data_buff+info->buf_start));
>> - ? ? ? ? ? ? ? info->buf_start += 2;
>> + ? ? ? if (!(nand->buf_start & 0x01) && nand->buf_start < nand->buf_count) {
>> + ? ? ? ? ? ? ? retval = *((u16 *)(nand->data_buff+nand->buf_start));
>> + ? ? ? ? ? ? ? nand->buf_start += 2;
>> ? ? ? ?}
>> ? ? ? ?return retval;
>> ?}
>> @@ -720,20 +753,22 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
>> ?static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
>> ?{
>> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
>> - ? ? ? int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? ? int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>>
>> - ? ? ? memcpy(buf, info->data_buff + info->buf_start, real_len);
>> - ? ? ? info->buf_start += real_len;
>> + ? ? ? memcpy(buf, nand->data_buff + nand->buf_start, real_len);
>> + ? ? ? nand->buf_start += real_len;
>> ?}
>>
>> ?static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
>> ? ? ? ? ? ? ? ?const uint8_t *buf, int len)
>> ?{
>> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
>> - ? ? ? int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? ? int real_len = min_t(size_t, len, nand->buf_count - nand->buf_start);
>>
>> - ? ? ? memcpy(info->data_buff + info->buf_start, buf, real_len);
>> - ? ? ? info->buf_start += real_len;
>> + ? ? ? memcpy(nand->data_buff + nand->buf_start, buf, real_len);
>> + ? ? ? nand->buf_start += real_len;
>> ?}
>>
>> ?static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
>> @@ -750,10 +785,11 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
>> ?static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>> ?{
>> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>>
>> ? ? ? ?/* pxa3xx_nand_send_command has waited for command complete */
>> ? ? ? ?if (this->state == FL_WRITING || this->state == FL_ERASING) {
>> - ? ? ? ? ? ? ? if (info->retcode == ERR_NONE)
>> + ? ? ? ? ? ? ? if (nand->retcode == ERR_NONE)
>> ? ? ? ? ? ? ? ? ? ? ? ?return 0;
>> ? ? ? ? ? ? ? ?else {
>> ? ? ? ? ? ? ? ? ? ? ? ?/*
>> @@ -770,7 +806,8 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
>> ?static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?const struct pxa3xx_nand_flash *f)
>> ?{
>> - ? ? ? struct platform_device *pdev = info->pdev;
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? ? struct platform_device *pdev = nand->pdev;
>> ? ? ? ?struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>> ? ? ? ?uint32_t ndcr = 0x0; /* enable all interrupts */
>>
>> @@ -804,6 +841,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>> ? ? ? ?ndcr |= NDCR_SPARE_EN; /* enable spare by default */
>>
>> ? ? ? ?info->reg_ndcr = ndcr;
>> + ? ? ? info->use_ecc = 1;
>>
>> ? ? ? ?pxa3xx_nand_set_timing(info, f->timing);
>> ? ? ? ?return 0;
>> @@ -811,15 +849,22 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>>
>> ?static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>> ?{
>> - ? ? ? uint32_t ndcr = nand_readl(info, NDCR);
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? ? uint32_t ndcr = nand_readl(nand, NDCR);
>> +
>> + ? ? ? if (info->chip_select > 0) {
>> + ? ? ? ? ? ? ? printk(KERN_ERR "We could not detect configure"
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " if more than one cs is supported!!\n");
>> + ? ? ? ? ? ? ? BUG();
>> + ? ? ? }
>> ? ? ? ?info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
>> ? ? ? ?/* set info fields needed to read id */
>> ? ? ? ?info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
>> ? ? ? ?info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
>> ? ? ? ?info->cmdset = &default_cmdset;
>>
>> - ? ? ? info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
>> - ? ? ? info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
>> + ? ? ? info->ndtr0cs0 = nand_readl(nand, NDTR0CS0);
>> + ? ? ? info->ndtr1cs0 = nand_readl(nand, NDTR1CS0);
>>
>> ? ? ? ?return 0;
>> ?}
>> @@ -830,50 +875,31 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
>> ?*/
>> ?#define MAX_BUFF_SIZE ?PAGE_SIZE
>>
>> -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
>> +static void free_cs_resource(struct pxa3xx_nand_info *info, int cs)
>> ?{
>> - ? ? ? struct platform_device *pdev = info->pdev;
>> - ? ? ? int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
>> -
>> - ? ? ? if (use_dma == 0) {
>> - ? ? ? ? ? ? ? info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
>> - ? ? ? ? ? ? ? if (info->data_buff == NULL)
>> - ? ? ? ? ? ? ? ? ? ? ? return -ENOMEM;
>> - ? ? ? ? ? ? ? return 0;
>> - ? ? ? }
>> -
>> - ? ? ? info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &info->data_buff_phys, GFP_KERNEL);
>> - ? ? ? if (info->data_buff == NULL) {
>> - ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to allocate dma buffer\n");
>> - ? ? ? ? ? ? ? return -ENOMEM;
>> - ? ? ? }
>> -
>> - ? ? ? info->data_buff_size = MAX_BUFF_SIZE;
>> - ? ? ? info->data_desc = (void *)info->data_buff + data_desc_offset;
>> - ? ? ? info->data_desc_addr = info->data_buff_phys + data_desc_offset;
>> + ? ? ? struct pxa3xx_nand *nand;
>> + ? ? ? struct mtd_info *mtd;
>>
>> - ? ? ? info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pxa3xx_nand_data_dma_irq, info);
>> - ? ? ? if (info->data_dma_ch < 0) {
>> - ? ? ? ? ? ? ? dev_err(&pdev->dev, "failed to request data dma\n");
>> - ? ? ? ? ? ? ? dma_free_coherent(&pdev->dev, info->data_buff_size,
>> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->data_buff, info->data_buff_phys);
>> - ? ? ? ? ? ? ? return info->data_dma_ch;
>> - ? ? ? }
>> + ? ? ? if (!info)
>> + ? ? ? ? ? ? ? return;
>>
>> - ? ? ? return 0;
>> + ? ? ? nand = info->nand_data;
>> + ? ? ? mtd = get_mtd_by_info(info);
>> + ? ? ? kfree(mtd);
>> + ? ? ? nand->info[cs] = NULL;
>> ?}
>>
>> ?static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>> ?{
>> - ? ? ? struct mtd_info *mtd = info->mtd;
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? ? struct mtd_info *mtd = get_mtd_by_info(info);
>> ? ? ? ?struct nand_chip *chip = mtd->priv;
>>
>> ? ? ? ?/* use the common timing to make a try */
>> - ? ? ? pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
>> + ? ? ? if (pxa3xx_nand_config_flash(info, &builtin_flash_types[0]))
>> + ? ? ? ? ? ? ? return 0;
>> ? ? ? ?chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
>> - ? ? ? if (info->is_ready)
>> + ? ? ? if (nand->is_ready)
>> ? ? ? ? ? ? ? ?return 1;
>> ? ? ? ?else
>> ? ? ? ? ? ? ? ?return 0;
>> @@ -882,7 +908,8 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
>> ?static int pxa3xx_nand_scan(struct mtd_info *mtd)
>> ?{
>> ? ? ? ?struct pxa3xx_nand_info *info = mtd->priv;
>> - ? ? ? struct platform_device *pdev = info->pdev;
>> + ? ? ? struct pxa3xx_nand *nand = info->nand_data;
>> + ? ? ? struct platform_device *pdev = nand->pdev;
>> ? ? ? ?struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>> ? ? ? ?struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
>> ? ? ? ?const struct pxa3xx_nand_flash *f = NULL;
>> @@ -891,27 +918,25 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>> ? ? ? ?uint64_t chipsize;
>> ? ? ? ?int i, ret, num;
>>
>> + ? ? ? nand->chip_select = info->chip_select;
>> ? ? ? ?if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
>> ? ? ? ? ? ? ? ?goto KEEP_CONFIG;
>>
>> ? ? ? ?ret = pxa3xx_nand_sensing(info);
>> ? ? ? ?if (!ret) {
>> - ? ? ? ? ? ? ? kfree(mtd);
>> - ? ? ? ? ? ? ? info->mtd = NULL;
>> - ? ? ? ? ? ? ? printk(KERN_INFO "There is no nand chip on cs 0!\n");
>> + ? ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
>> + ? ? ? ? ? ? ? printk(KERN_INFO "There is no nand chip on cs %d!\n",
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand->chip_select);
>
> dev_err()?
>
>>
>> ? ? ? ? ? ? ? ?return -EINVAL;
>> ? ? ? ?}
>>
>> ? ? ? ?chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>> - ? ? ? id = *((uint16_t *)(info->data_buff));
>> + ? ? ? id = *((uint16_t *)(nand->data_buff));
>> ? ? ? ?if (id != 0)
>> ? ? ? ? ? ? ? ?printk(KERN_INFO "Detect a flash id %x\n", id);
>> ? ? ? ?else {
>> - ? ? ? ? ? ? ? kfree(mtd);
>> - ? ? ? ? ? ? ? info->mtd = NULL;
>> - ? ? ? ? ? ? ? printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
>> -
>> + ? ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
>> ? ? ? ? ? ? ? ?return -EINVAL;
>> ? ? ? ?}
>>
>> @@ -928,14 +953,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>> ? ? ? ?}
>>
>> ? ? ? ?if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
>> - ? ? ? ? ? ? ? kfree(mtd);
>> - ? ? ? ? ? ? ? info->mtd = NULL;
>> + ? ? ? ? ? ? ? free_cs_resource(info, nand->chip_select);
>> ? ? ? ? ? ? ? ?printk(KERN_ERR "ERROR!! flash not defined!!!\n");
>
> Also here.
>
>>
>> ? ? ? ? ? ? ? ?return -EINVAL;
>> ? ? ? ?}
>>
>> - ? ? ? pxa3xx_nand_config_flash(info, f);
>> + ? ? ? if (pxa3xx_nand_config_flash(info, f)) {
>> + ? ? ? ? ? ? ? printk(KERN_ERR "ERROR! Configure failed\n");
>
> And here. Giving no hint about which driver or function causes this
> message makes debugging harder than it should be.
>
> [...]
>
>> ?#ifdef CONFIG_PM
>> @@ -1183,8 +1278,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>> ? ? ? ?struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
>> ? ? ? ?struct mtd_info *mtd = info->mtd;
>>
>> - ? ? ? nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>> - ? ? ? nand_writel(info, NDTR1CS0, info->ndtr1cs0);
>> + ? ? ? nand_writel(nand, NDTR0CS0, info->ndtr0cs0);
>> + ? ? ? nand_writel(nand, NDTR1CS0, info->ndtr1cs0);
>> ? ? ? ?clk_enable(info->clk);
>
> This won't compile.

Could you post your build log?

Thanks,
Lei

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-06  7:41                 ` Igor Grinberg
@ 2011-07-11 14:49                   ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-11 14:49 UTC (permalink / raw)
  To: Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

On Wed, Jul 6, 2011 at 9:41 AM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> Daniel,
>
> have you tested the suspend/resume with this patch?
> (and keep_config on?)

We did now test this with keep_config=1 and it seems that we indeed
have a problem with suspend/resume after this patch. Affected machines
show the following after wake up:


[ 1295.458958] PM: resume of devices complete after 296.228 msecs
[ 1295.717773] Restarting tasks ...
[ 1295.729639] UBI error: ubi_io_write: error -5 while writing 2048
bytes to PEB 19:59392, written 0 bytes
[ 1295.761672] done.
[ 1295.772706] UBI warning: ubi_eba_write_leb: failed to write data to PEB 19
[ 1295.782492] UBI: recover PEB 19, move data to PEB 405
[ 1295.849084] mmc0: new SDIO card at address 0001
[ 1295.978045] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1295.983153] UBI warning: ubi_io_read_vid_hdr: no VID header found
at PEB 19, only 0xFF bytes
[ 1296.015382] UBI: run torture test for PEB 405
[ 1296.024375] UBI warning: ubi_ro_mode: switch to read-only mode
[ 1296.032321] UBI error: do_sync_erase: read-only mode
[ 1296.037296] UBI error: erase_worker: failed to erase PEB 405, error -30
[ 1296.058363] UBIFS error (pid 1303): ubifs_wbuf_write_nolock: cannot
write 424 bytes to LEB 317:55296, error -5
[ 1296.080917] UBIFS error (pid 1092): ubifs_wbuf_write_nolock: cannot
write 416 bytes to LEB 317:55296, error -30
[ 1296.094470] UBIFS warning (pid 1303): ubifs_ro_mode: switched to
read-only mode, error -5
[ 1296.106183] UBIFS error (pid 1092): ubifs_create: cannot create
regular file, error -30
[ 1296.118007] UBI error: do_work: work failed with error code -30
[ 1296.123915] UBI error: ubi_thread: ubi_bgt0d: work failed with error code -30
[ 1296.742934] libertas_sdio mmc0:0001:1: (unregistered net_device):
00:19:88:11:db:67, fw 9.70.7p0, cap 0x00000303
[ 1296.779845] libertas_sdio mmc0:0001:1: wlan0: Marvell WLAN 802.11 adapter
[ 1296.847141] ADDRCONF(NETDEV_UP): wlan0: link is not ready
[ 1298.041988] ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
[ 1300.957951] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.157810] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.357874] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.557924] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.563064] UBIFS error (pid 1260): ubifs_read_node: bad node type
(255 but expected 1)
[ 1301.573526] UBIFS error (pid 1260): ubifs_read_node: bad node at
LEB 112:18808, LEB mapping status 1
[ 1301.583586] UBIFS error (pid 1260): do_readpage: cannot read page 3
of inode 10295, error -22
[ 1301.787877] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.987891] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1302.187878] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1302.387878] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1302.393866] UBIFS error (pid 1260): ubifs_read_node: bad node type
(255 but expected 1)
[ 1302.403452] UBIFS error (pid 1260): ubifs_read_node: bad node at
LEB 112:18808, LEB mapping status 1
[ 1302.413507] UBIFS error (pid 1260): do_readpage: cannot read page 3
of inode 10295, error -22


The UBIFS is very likely toast after this.

Lei, any ideas, can you reproduce this? To avoid further regressions
of this driver, you really should have a system on which you can test
suspend and resume after each commit.


Thanks,
Daniel

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-11 14:49                   ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-11 14:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jul 6, 2011 at 9:41 AM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> Daniel,
>
> have you tested the suspend/resume with this patch?
> (and keep_config on?)

We did now test this with keep_config=1 and it seems that we indeed
have a problem with suspend/resume after this patch. Affected machines
show the following after wake up:


[ 1295.458958] PM: resume of devices complete after 296.228 msecs
[ 1295.717773] Restarting tasks ...
[ 1295.729639] UBI error: ubi_io_write: error -5 while writing 2048
bytes to PEB 19:59392, written 0 bytes
[ 1295.761672] done.
[ 1295.772706] UBI warning: ubi_eba_write_leb: failed to write data to PEB 19
[ 1295.782492] UBI: recover PEB 19, move data to PEB 405
[ 1295.849084] mmc0: new SDIO card at address 0001
[ 1295.978045] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1295.983153] UBI warning: ubi_io_read_vid_hdr: no VID header found
at PEB 19, only 0xFF bytes
[ 1296.015382] UBI: run torture test for PEB 405
[ 1296.024375] UBI warning: ubi_ro_mode: switch to read-only mode
[ 1296.032321] UBI error: do_sync_erase: read-only mode
[ 1296.037296] UBI error: erase_worker: failed to erase PEB 405, error -30
[ 1296.058363] UBIFS error (pid 1303): ubifs_wbuf_write_nolock: cannot
write 424 bytes to LEB 317:55296, error -5
[ 1296.080917] UBIFS error (pid 1092): ubifs_wbuf_write_nolock: cannot
write 416 bytes to LEB 317:55296, error -30
[ 1296.094470] UBIFS warning (pid 1303): ubifs_ro_mode: switched to
read-only mode, error -5
[ 1296.106183] UBIFS error (pid 1092): ubifs_create: cannot create
regular file, error -30
[ 1296.118007] UBI error: do_work: work failed with error code -30
[ 1296.123915] UBI error: ubi_thread: ubi_bgt0d: work failed with error code -30
[ 1296.742934] libertas_sdio mmc0:0001:1: (unregistered net_device):
00:19:88:11:db:67, fw 9.70.7p0, cap 0x00000303
[ 1296.779845] libertas_sdio mmc0:0001:1: wlan0: Marvell WLAN 802.11 adapter
[ 1296.847141] ADDRCONF(NETDEV_UP): wlan0: link is not ready
[ 1298.041988] ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
[ 1300.957951] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.157810] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.357874] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.557924] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.563064] UBIFS error (pid 1260): ubifs_read_node: bad node type
(255 but expected 1)
[ 1301.573526] UBIFS error (pid 1260): ubifs_read_node: bad node at
LEB 112:18808, LEB mapping status 1
[ 1301.583586] UBIFS error (pid 1260): do_readpage: cannot read page 3
of inode 10295, error -22
[ 1301.787877] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1301.987891] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1302.187878] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1302.387878] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1302.393866] UBIFS error (pid 1260): ubifs_read_node: bad node type
(255 but expected 1)
[ 1302.403452] UBIFS error (pid 1260): ubifs_read_node: bad node at
LEB 112:18808, LEB mapping status 1
[ 1302.413507] UBIFS error (pid 1260): do_readpage: cannot read page 3
of inode 10295, error -22


The UBIFS is very likely toast after this.

Lei, any ideas, can you reproduce this? To avoid further regressions
of this driver, you really should have a system on which you can test
suspend and resume after each commit.


Thanks,
Daniel

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-11 14:49                   ` Daniel Mack
@ 2011-07-11 18:19                     ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-11 18:19 UTC (permalink / raw)
  To: Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

On 07/11/11 17:49, Daniel Mack wrote:
> On Wed, Jul 6, 2011 at 9:41 AM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> Daniel,
>>
>> have you tested the suspend/resume with this patch?
>> (and keep_config on?)
> We did now test this with keep_config=1 and it seems that we indeed
> have a problem with suspend/resume after this patch. Affected machines
> show the following after wake up:
>
>
> [ 1295.458958] PM: resume of devices complete after 296.228 msecs
> [ 1295.717773] Restarting tasks ...
> [ 1295.729639] UBI error: ubi_io_write: error -5 while writing 2048
> bytes to PEB 19:59392, written 0 bytes
> [ 1295.761672] done.
> [ 1295.772706] UBI warning: ubi_eba_write_leb: failed to write data to PEB 19
> [ 1295.782492] UBI: recover PEB 19, move data to PEB 405
> [ 1295.849084] mmc0: new SDIO card at address 0001
> [ 1295.978045] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1295.983153] UBI warning: ubi_io_read_vid_hdr: no VID header found
> at PEB 19, only 0xFF bytes
> [ 1296.015382] UBI: run torture test for PEB 405
> [ 1296.024375] UBI warning: ubi_ro_mode: switch to read-only mode
> [ 1296.032321] UBI error: do_sync_erase: read-only mode
> [ 1296.037296] UBI error: erase_worker: failed to erase PEB 405, error -30
> [ 1296.058363] UBIFS error (pid 1303): ubifs_wbuf_write_nolock: cannot
> write 424 bytes to LEB 317:55296, error -5
> [ 1296.080917] UBIFS error (pid 1092): ubifs_wbuf_write_nolock: cannot
> write 416 bytes to LEB 317:55296, error -30
> [ 1296.094470] UBIFS warning (pid 1303): ubifs_ro_mode: switched to
> read-only mode, error -5
> [ 1296.106183] UBIFS error (pid 1092): ubifs_create: cannot create
> regular file, error -30
> [ 1296.118007] UBI error: do_work: work failed with error code -30
> [ 1296.123915] UBI error: ubi_thread: ubi_bgt0d: work failed with error code -30
> [ 1296.742934] libertas_sdio mmc0:0001:1: (unregistered net_device):
> 00:19:88:11:db:67, fw 9.70.7p0, cap 0x00000303
> [ 1296.779845] libertas_sdio mmc0:0001:1: wlan0: Marvell WLAN 802.11 adapter
> [ 1296.847141] ADDRCONF(NETDEV_UP): wlan0: link is not ready
> [ 1298.041988] ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
> [ 1300.957951] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.157810] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.357874] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.557924] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.563064] UBIFS error (pid 1260): ubifs_read_node: bad node type
> (255 but expected 1)
> [ 1301.573526] UBIFS error (pid 1260): ubifs_read_node: bad node at
> LEB 112:18808, LEB mapping status 1
> [ 1301.583586] UBIFS error (pid 1260): do_readpage: cannot read page 3
> of inode 10295, error -22
> [ 1301.787877] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.987891] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1302.187878] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1302.387878] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1302.393866] UBIFS error (pid 1260): ubifs_read_node: bad node type
> (255 but expected 1)
> [ 1302.403452] UBIFS error (pid 1260): ubifs_read_node: bad node at
> LEB 112:18808, LEB mapping status 1
> [ 1302.413507] UBIFS error (pid 1260): do_readpage: cannot read page 3
> of inode 10295, error -22
>
>
> The UBIFS is very likely toast after this.

Heh, so I'm still sane... ;)

> Lei, any ideas, can you reproduce this? To avoid further regressions
> of this driver, you really should have a system on which you can test
> suspend and resume after each commit.

I think the problem is that on PXA3xx resume from S2/S3 involves bootloader.
If I remember correctly, the BootROM reads the bootloader from the boot device
regardless of the reset cause (S2/S3 resume is kind a reset for PXA3xx).
Then the bootloader must decide what should be done according to the reset cause.
This means, that the BootROM already configures the NAND flash
(if it is the boot device) and pxa3xx_nand driver should just get on with it
and don't try to reconfigure?

Am I still sane? ;)

-- 
Regards,
Igor.

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-11 18:19                     ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-11 18:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/11/11 17:49, Daniel Mack wrote:
> On Wed, Jul 6, 2011 at 9:41 AM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> Daniel,
>>
>> have you tested the suspend/resume with this patch?
>> (and keep_config on?)
> We did now test this with keep_config=1 and it seems that we indeed
> have a problem with suspend/resume after this patch. Affected machines
> show the following after wake up:
>
>
> [ 1295.458958] PM: resume of devices complete after 296.228 msecs
> [ 1295.717773] Restarting tasks ...
> [ 1295.729639] UBI error: ubi_io_write: error -5 while writing 2048
> bytes to PEB 19:59392, written 0 bytes
> [ 1295.761672] done.
> [ 1295.772706] UBI warning: ubi_eba_write_leb: failed to write data to PEB 19
> [ 1295.782492] UBI: recover PEB 19, move data to PEB 405
> [ 1295.849084] mmc0: new SDIO card at address 0001
> [ 1295.978045] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1295.983153] UBI warning: ubi_io_read_vid_hdr: no VID header found
> at PEB 19, only 0xFF bytes
> [ 1296.015382] UBI: run torture test for PEB 405
> [ 1296.024375] UBI warning: ubi_ro_mode: switch to read-only mode
> [ 1296.032321] UBI error: do_sync_erase: read-only mode
> [ 1296.037296] UBI error: erase_worker: failed to erase PEB 405, error -30
> [ 1296.058363] UBIFS error (pid 1303): ubifs_wbuf_write_nolock: cannot
> write 424 bytes to LEB 317:55296, error -5
> [ 1296.080917] UBIFS error (pid 1092): ubifs_wbuf_write_nolock: cannot
> write 416 bytes to LEB 317:55296, error -30
> [ 1296.094470] UBIFS warning (pid 1303): ubifs_ro_mode: switched to
> read-only mode, error -5
> [ 1296.106183] UBIFS error (pid 1092): ubifs_create: cannot create
> regular file, error -30
> [ 1296.118007] UBI error: do_work: work failed with error code -30
> [ 1296.123915] UBI error: ubi_thread: ubi_bgt0d: work failed with error code -30
> [ 1296.742934] libertas_sdio mmc0:0001:1: (unregistered net_device):
> 00:19:88:11:db:67, fw 9.70.7p0, cap 0x00000303
> [ 1296.779845] libertas_sdio mmc0:0001:1: wlan0: Marvell WLAN 802.11 adapter
> [ 1296.847141] ADDRCONF(NETDEV_UP): wlan0: link is not ready
> [ 1298.041988] ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
> [ 1300.957951] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.157810] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.357874] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.557924] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.563064] UBIFS error (pid 1260): ubifs_read_node: bad node type
> (255 but expected 1)
> [ 1301.573526] UBIFS error (pid 1260): ubifs_read_node: bad node at
> LEB 112:18808, LEB mapping status 1
> [ 1301.583586] UBIFS error (pid 1260): do_readpage: cannot read page 3
> of inode 10295, error -22
> [ 1301.787877] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1301.987891] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1302.187878] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1302.387878] pxa3xx-nand pxa3xx-nand: Wait time out!!!
> [ 1302.393866] UBIFS error (pid 1260): ubifs_read_node: bad node type
> (255 but expected 1)
> [ 1302.403452] UBIFS error (pid 1260): ubifs_read_node: bad node at
> LEB 112:18808, LEB mapping status 1
> [ 1302.413507] UBIFS error (pid 1260): do_readpage: cannot read page 3
> of inode 10295, error -22
>
>
> The UBIFS is very likely toast after this.

Heh, so I'm still sane... ;)

> Lei, any ideas, can you reproduce this? To avoid further regressions
> of this driver, you really should have a system on which you can test
> suspend and resume after each commit.

I think the problem is that on PXA3xx resume from S2/S3 involves bootloader.
If I remember correctly, the BootROM reads the bootloader from the boot device
regardless of the reset cause (S2/S3 resume is kind a reset for PXA3xx).
Then the bootloader must decide what should be done according to the reset cause.
This means, that the BootROM already configures the NAND flash
(if it is the boot device) and pxa3xx_nand driver should just get on with it
and don't try to reconfigure?

Am I still sane? ;)

-- 
Regards,
Igor.

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-11 18:19                     ` Igor Grinberg
@ 2011-07-11 18:53                       ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-11 18:53 UTC (permalink / raw)
  To: Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

On Mon, Jul 11, 2011 at 8:19 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/11/11 17:49, Daniel Mack wrote:
>> Lei, any ideas, can you reproduce this? To avoid further regressions
>> of this driver, you really should have a system on which you can test
>> suspend and resume after each commit.
>
> I think the problem is that on PXA3xx resume from S2/S3 involves bootloader.
> If I remember correctly, the BootROM reads the bootloader from the boot device
> regardless of the reset cause (S2/S3 resume is kind a reset for PXA3xx).

At least not in our case. The first level bootloader is entered on
resume just as it is on a POR event, with the exception that the D3S
bit in the ASCR register is set in this case. We unconditionally
initialize the static and dynamic memory controllers and then either
jump to the routine that initializes the NAND controller, read the 2nd
level loader and pass control to it. Or (in the resume case) we just
jump to the address stored at the RAM address 0x80000000 (which has
been set to cpu_resume previously) and thus enter the kernel again. No
NAND operations in the game in this case, and this has always worked
fine.

> Then the bootloader must decide what should be done according to the reset cause.
> This means, that the BootROM already configures the NAND flash
> (if it is the boot device) and pxa3xx_nand driver should just get on with it
> and don't try to reconfigure?

I think the problem is just the opposite. The driver expects the setup
registers to retain their state over suspend, and doesn't write them
again, and this causes operations to fail after resume.


Daniel

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-11 18:53                       ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-11 18:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jul 11, 2011 at 8:19 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/11/11 17:49, Daniel Mack wrote:
>> Lei, any ideas, can you reproduce this? To avoid further regressions
>> of this driver, you really should have a system on which you can test
>> suspend and resume after each commit.
>
> I think the problem is that on PXA3xx resume from S2/S3 involves bootloader.
> If I remember correctly, the BootROM reads the bootloader from the boot device
> regardless of the reset cause (S2/S3 resume is kind a reset for PXA3xx).

At least not in our case. The first level bootloader is entered on
resume just as it is on a POR event, with the exception that the D3S
bit in the ASCR register is set in this case. We unconditionally
initialize the static and dynamic memory controllers and then either
jump to the routine that initializes the NAND controller, read the 2nd
level loader and pass control to it. Or (in the resume case) we just
jump to the address stored at the RAM address 0x80000000 (which has
been set to cpu_resume previously) and thus enter the kernel again. No
NAND operations in the game in this case, and this has always worked
fine.

> Then the bootloader must decide what should be done according to the reset cause.
> This means, that the BootROM already configures the NAND flash
> (if it is the boot device) and pxa3xx_nand driver should just get on with it
> and don't try to reconfigure?

I think the problem is just the opposite. The driver expects the setup
registers to retain their state over suspend, and doesn't write them
again, and this causes operations to fail after resume.


Daniel

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-11 18:53                       ` Daniel Mack
@ 2011-07-11 19:25                         ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-11 19:25 UTC (permalink / raw)
  To: Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel



On 07/11/11 21:53, Daniel Mack wrote:

> On Mon, Jul 11, 2011 at 8:19 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 07/11/11 17:49, Daniel Mack wrote:
>>> Lei, any ideas, can you reproduce this? To avoid further regressions
>>> of this driver, you really should have a system on which you can test
>>> suspend and resume after each commit.
>> I think the problem is that on PXA3xx resume from S2/S3 involves bootloader.
>> If I remember correctly, the BootROM reads the bootloader from the boot device
>> regardless of the reset cause (S2/S3 resume is kind a reset for PXA3xx).
> At least not in our case. The first level bootloader is entered on
> resume just as it is on a POR event, with the exception that the D3S
> bit in the ASCR register is set in this case.

You say "first level boot loader is entered"... who is loading it and where from?
Indeed, it should happen as in POR case, but again, it resides on some non-volatile
storage, isn't it? What storage does it reside on?

> We unconditionally
> initialize the static and dynamic memory controllers and then either
> jump to the routine that initializes the NAND controller, read the 2nd
> level loader and pass control to it. Or (in the resume case) we just
> jump to the address stored at the RAM address 0x80000000 (which has
> been set to cpu_resume previously) and thus enter the kernel again. No
> NAND operations in the game in this case, and this has always worked
> fine.

Except those to read the first stage boot loader... (if it resides on NAND).
Well, our case is working similarly and both the first stage bootloader (OBM)
and the second stage bootloader (U-Boot) reside on the NAND flash,
therefore BootROM has to reconfigure the NAND flash, so it can boot (resume).


>> Then the bootloader must decide what should be done according to the reset cause.
>> This means, that the BootROM already configures the NAND flash
>> (if it is the boot device) and pxa3xx_nand driver should just get on with it
>> and don't try to reconfigure?
> I think the problem is just the opposite. The driver expects the setup
> registers to retain their state over suspend, and doesn't write them
> again, and this causes operations to fail after resume.

Well, according to Lei, the driver writes back the configuration that is stored prior
to suspend and that's why I agreed that it should work...

http://www.spinics.net/lists/arm-kernel/msg131942.html



-- 
Regards,
Igor.

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-11 19:25                         ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-11 19:25 UTC (permalink / raw)
  To: linux-arm-kernel



On 07/11/11 21:53, Daniel Mack wrote:

> On Mon, Jul 11, 2011 at 8:19 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 07/11/11 17:49, Daniel Mack wrote:
>>> Lei, any ideas, can you reproduce this? To avoid further regressions
>>> of this driver, you really should have a system on which you can test
>>> suspend and resume after each commit.
>> I think the problem is that on PXA3xx resume from S2/S3 involves bootloader.
>> If I remember correctly, the BootROM reads the bootloader from the boot device
>> regardless of the reset cause (S2/S3 resume is kind a reset for PXA3xx).
> At least not in our case. The first level bootloader is entered on
> resume just as it is on a POR event, with the exception that the D3S
> bit in the ASCR register is set in this case.

You say "first level boot loader is entered"... who is loading it and where from?
Indeed, it should happen as in POR case, but again, it resides on some non-volatile
storage, isn't it? What storage does it reside on?

> We unconditionally
> initialize the static and dynamic memory controllers and then either
> jump to the routine that initializes the NAND controller, read the 2nd
> level loader and pass control to it. Or (in the resume case) we just
> jump to the address stored at the RAM address 0x80000000 (which has
> been set to cpu_resume previously) and thus enter the kernel again. No
> NAND operations in the game in this case, and this has always worked
> fine.

Except those to read the first stage boot loader... (if it resides on NAND).
Well, our case is working similarly and both the first stage bootloader (OBM)
and the second stage bootloader (U-Boot) reside on the NAND flash,
therefore BootROM has to reconfigure the NAND flash, so it can boot (resume).


>> Then the bootloader must decide what should be done according to the reset cause.
>> This means, that the BootROM already configures the NAND flash
>> (if it is the boot device) and pxa3xx_nand driver should just get on with it
>> and don't try to reconfigure?
> I think the problem is just the opposite. The driver expects the setup
> registers to retain their state over suspend, and doesn't write them
> again, and this causes operations to fail after resume.

Well, according to Lei, the driver writes back the configuration that is stored prior
to suspend and that's why I agreed that it should work...

http://www.spinics.net/lists/arm-kernel/msg131942.html



-- 
Regards,
Igor.

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-11 19:25                         ` Igor Grinberg
@ 2011-07-12  9:40                           ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12  9:40 UTC (permalink / raw)
  To: Daniel Mack, Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

On Tue, Jul 12, 2011 at 3:25 AM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>
>
> On 07/11/11 21:53, Daniel Mack wrote:
>
>> On Mon, Jul 11, 2011 at 8:19 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>>> On 07/11/11 17:49, Daniel Mack wrote:
>>>> Lei, any ideas, can you reproduce this? To avoid further regressions
>>>> of this driver, you really should have a system on which you can test
>>>> suspend and resume after each commit.
>>> I think the problem is that on PXA3xx resume from S2/S3 involves bootloader.
>>> If I remember correctly, the BootROM reads the bootloader from the boot device
>>> regardless of the reset cause (S2/S3 resume is kind a reset for PXA3xx).
>> At least not in our case. The first level bootloader is entered on
>> resume just as it is on a POR event, with the exception that the D3S
>> bit in the ASCR register is set in this case.
>
> You say "first level boot loader is entered"... who is loading it and where from?
> Indeed, it should happen as in POR case, but again, it resides on some non-volatile
> storage, isn't it? What storage does it reside on?
>
>> We unconditionally
>> initialize the static and dynamic memory controllers and then either
>> jump to the routine that initializes the NAND controller, read the 2nd
>> level loader and pass control to it. Or (in the resume case) we just
>> jump to the address stored at the RAM address 0x80000000 (which has
>> been set to cpu_resume previously) and thus enter the kernel again. No
>> NAND operations in the game in this case, and this has always worked
>> fine.
>
> Except those to read the first stage boot loader... (if it resides on NAND).
> Well, our case is working similarly and both the first stage bootloader (OBM)
> and the second stage bootloader (U-Boot) reside on the NAND flash,
> therefore BootROM has to reconfigure the NAND flash, so it can boot (resume).
>
>
>>> Then the bootloader must decide what should be done according to the reset cause.
>>> This means, that the BootROM already configures the NAND flash
>>> (if it is the boot device) and pxa3xx_nand driver should just get on with it
>>> and don't try to reconfigure?
>> I think the problem is just the opposite. The driver expects the setup
>> registers to retain their state over suspend, and doesn't write them
>> again, and this causes operations to fail after resume.
>
> Well, according to Lei, the driver writes back the configuration that is stored prior
> to suspend and that's why I agreed that it should work...
>
> http://www.spinics.net/lists/arm-kernel/msg131942.html
>
>
Daniel,

I retest with suspend&resume on my board, each time that board would
reset the timing in the
begining of pxa3xx_cmdfunc as expected. So this should be ok as it is
the same behavior
with original code.

However, it certainly has some potential risk in the suspend&resume process.
That is in pxa3xx_nand_suspend and pxa3xx_nand_resume, it should call
mtd->suspend and mtd->resume
respectively.

This implement has already existed in our code release for years, but
have get into the mainline yet.
So I tend to merged into the third patch, and you could have test
again whether could fix your issue.

Thanks,
Lei

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-12  9:40                           ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12  9:40 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 12, 2011 at 3:25 AM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>
>
> On 07/11/11 21:53, Daniel Mack wrote:
>
>> On Mon, Jul 11, 2011 at 8:19 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>>> On 07/11/11 17:49, Daniel Mack wrote:
>>>> Lei, any ideas, can you reproduce this? To avoid further regressions
>>>> of this driver, you really should have a system on which you can test
>>>> suspend and resume after each commit.
>>> I think the problem is that on PXA3xx resume from S2/S3 involves bootloader.
>>> If I remember correctly, the BootROM reads the bootloader from the boot device
>>> regardless of the reset cause (S2/S3 resume is kind a reset for PXA3xx).
>> At least not in our case. The first level bootloader is entered on
>> resume just as it is on a POR event, with the exception that the D3S
>> bit in the ASCR register is set in this case.
>
> You say "first level boot loader is entered"... who is loading it and where from?
> Indeed, it should happen as in POR case, but again, it resides on some non-volatile
> storage, isn't it? What storage does it reside on?
>
>> We unconditionally
>> initialize the static and dynamic memory controllers and then either
>> jump to the routine that initializes the NAND controller, read the 2nd
>> level loader and pass control to it. Or (in the resume case) we just
>> jump to the address stored at the RAM address 0x80000000 (which has
>> been set to cpu_resume previously) and thus enter the kernel again. No
>> NAND operations in the game in this case, and this has always worked
>> fine.
>
> Except those to read the first stage boot loader... (if it resides on NAND).
> Well, our case is working similarly and both the first stage bootloader (OBM)
> and the second stage bootloader (U-Boot) reside on the NAND flash,
> therefore BootROM has to reconfigure the NAND flash, so it can boot (resume).
>
>
>>> Then the bootloader must decide what should be done according to the reset cause.
>>> This means, that the BootROM already configures the NAND flash
>>> (if it is the boot device) and pxa3xx_nand driver should just get on with it
>>> and don't try to reconfigure?
>> I think the problem is just the opposite. The driver expects the setup
>> registers to retain their state over suspend, and doesn't write them
>> again, and this causes operations to fail after resume.
>
> Well, according to Lei, the driver writes back the configuration that is stored prior
> to suspend and that's why I agreed that it should work...
>
> http://www.spinics.net/lists/arm-kernel/msg131942.html
>
>
Daniel,

I retest with suspend&resume on my board, each time that board would
reset the timing in the
begining of pxa3xx_cmdfunc as expected. So this should be ok as it is
the same behavior
with original code.

However, it certainly has some potential risk in the suspend&resume process.
That is in pxa3xx_nand_suspend and pxa3xx_nand_resume, it should call
mtd->suspend and mtd->resume
respectively.

This implement has already existed in our code release for years, but
have get into the mainline yet.
So I tend to merged into the third patch, and you could have test
again whether could fix your issue.

Thanks,
Lei

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-12  9:40                           ` Lei Wen
@ 2011-07-12  9:57                             ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12  9:57 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, Igor Grinberg, linux-arm-kernel

On Tue, Jul 12, 2011 at 11:40 AM, Lei Wen <adrian.wenl@gmail.com> wrote:
> I retest with suspend&resume on my board, each time that board would
> reset the timing in the
> begining of pxa3xx_cmdfunc as expected. So this should be ok as it is
> the same behavior
> with original code.

Ok, just to be sure - as there have been quite some iterations fot
this now - can you send me a link to your most current version of this
patch again, please? Just to avoid confusion which version we're
talking about.


Thanks,
Daniel

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-12  9:57                             ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12  9:57 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 12, 2011 at 11:40 AM, Lei Wen <adrian.wenl@gmail.com> wrote:
> I retest with suspend&resume on my board, each time that board would
> reset the timing in the
> begining of pxa3xx_cmdfunc as expected. So this should be ok as it is
> the same behavior
> with original code.

Ok, just to be sure - as there have been quite some iterations fot
this now - can you send me a link to your most current version of this
patch again, please? Just to avoid confusion which version we're
talking about.


Thanks,
Daniel

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

* [PATCH V6 0/4] pxa3xx_nand: add two chip select support
  2011-07-08  3:34                 ` Lei Wen
@ 2011-07-12 10:28                   ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

V5:
Merge platform change patch and two chip feature into one patch.
Clean some format issue.

V6:
Add suspend and resume enhancement as a part of this patch set.

Lei Wen (4):
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support
  MTD: pxa3xx_nand: enhance suspend and resume routine

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  452 ++++++++++++++++----------
 9 files changed, 327 insertions(+), 184 deletions(-)

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

* [PATCH V6 0/4] pxa3xx_nand: add two chip select support
@ 2011-07-12 10:28                   ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: linux-arm-kernel

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

V5:
Merge platform change patch and two chip feature into one patch.
Clean some format issue.

V6:
Add suspend and resume enhancement as a part of this patch set.

Lei Wen (4):
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support
  MTD: pxa3xx_nand: enhance suspend and resume routine

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  452 ++++++++++++++++----------
 9 files changed, 327 insertions(+), 184 deletions(-)

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

* [PATCH V6 1/4] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-07-08  3:34                 ` Lei Wen
@ 2011-07-12 10:28                   ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..043c0bb 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH V6 1/4] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-07-12 10:28                   ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..043c0bb 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH V6 2/4] MTD: pxa3xx_nand: sperate each chip individual info
  2011-07-08  3:34                 ` Lei Wen
@ 2011-07-12 10:28                   ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  292 ++++++++++++++++++++++------------------
 1 files changed, 160 insertions(+), 132 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 043c0bb..8067949 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
 
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,52 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +867,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +874,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +884,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +916,10 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +927,6 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -933,14 +946,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -952,47 +968,56 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	pxa3xx_flash_ids[1].name = NULL;
 	def = pxa3xx_flash_ids;
 KEEP_CONFIG:
+	chip->ecc.mode = NAND_ECC_HW;
+	chip->ecc.size = host->page_size;
+
+	chip->options = NAND_NO_AUTOINCR;
+	chip->options |= NAND_NO_READRDY;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
+
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
-	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
-
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
-	chip->options |= NAND_NO_AUTOINCR;
-	chip->options |= NAND_NO_READRDY;
+		host->row_addr_cycles = 2;
 
+	mtd->name = mtd_names[0];
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1025,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1103,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1121,19 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
+
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1141,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1153,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1162,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1170,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1176,8 +1204,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH V6 2/4] MTD: pxa3xx_nand: sperate each chip individual info
@ 2011-07-12 10:28                   ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: linux-arm-kernel

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  292 ++++++++++++++++++++++------------------
 1 files changed, 160 insertions(+), 132 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 043c0bb..8067949 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
 
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,52 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +867,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +874,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +884,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +916,10 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +927,6 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -933,14 +946,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -952,47 +968,56 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	pxa3xx_flash_ids[1].name = NULL;
 	def = pxa3xx_flash_ids;
 KEEP_CONFIG:
+	chip->ecc.mode = NAND_ECC_HW;
+	chip->ecc.size = host->page_size;
+
+	chip->options = NAND_NO_AUTOINCR;
+	chip->options |= NAND_NO_READRDY;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
+
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
-	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
-
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
-	chip->options |= NAND_NO_AUTOINCR;
-	chip->options |= NAND_NO_READRDY;
+		host->row_addr_cycles = 2;
 
+	mtd->name = mtd_names[0];
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1025,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1103,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1121,19 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
+
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1141,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1153,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1162,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1170,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1176,8 +1204,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH V6 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-08  3:34                 ` Lei Wen
@ 2011-07-12 10:28                   ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  151 ++++++++++++++++++--------
 9 files changed, 146 insertions(+), 64 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 8067949..3756d54 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -884,9 +915,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -917,7 +948,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
+			 info->cs);
 
 		return ret;
 	}
@@ -996,41 +1028,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1128,12 +1166,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1153,7 +1193,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1162,7 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1177,14 +1218,28 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1204,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH V6 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-12 10:28                   ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  151 ++++++++++++++++++--------
 9 files changed, 146 insertions(+), 64 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 8067949..3756d54 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -884,9 +915,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -917,7 +948,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
+			 info->cs);
 
 		return ret;
 	}
@@ -996,41 +1028,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1128,12 +1166,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1153,7 +1193,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1162,7 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1177,14 +1218,28 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1204,8 +1259,12 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 	clk_enable(info->clk);
 
 	return 0;
-- 
1.7.0.4

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

* [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
  2011-07-08  3:34                 ` Lei Wen
@ 2011-07-12 10:28                   ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

This patch add protection on the suspend&resume path to prevent
some unexpected behavior, like interrupt occur at the very second
of resume back and it don't follow normal command path, which lead
to bug.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   28 ++++++++++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 3756d54..35149fe 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1246,18 +1246,34 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
+	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
+	for (cs = 0; cs < pdata->num_cs; cs ++) {
+		mtd = info->host[cs]->mtd;
+		mtd->suspend(mtd);
+	}
+
+	clk_disable(info->clk);
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
+	int cs;
+
+	pdata = pdev->dev.platform_data;
+	/* We don't want to handle interrupt without calling mtd routine */
+	disable_int(info, NDCR_INT_MASK);
 
 	/*
 	 * Directly set the chip select to a invalid value,
@@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	info->cs = 0xff;
 	clk_enable(info->clk);
 
+	/*
+	 * As the spec, the NDSR would be updated to 0x1800 when
+	 * do the nand_clk disable/enable.
+	 * To prevent it damage state machine of the driver, clear
+	 * all status before resume
+	 */
+	nand_writel(nand, NDSR, NDSR_MASK);
+	for (cs = 0; cs < pdata->num_cs; cs ++) {
+		mtd = info->host[cs]->mtd;
+		mtd->resume(mtd);
+	}
+
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
@ 2011-07-12 10:28                   ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:28 UTC (permalink / raw)
  To: linux-arm-kernel

This patch add protection on the suspend&resume path to prevent
some unexpected behavior, like interrupt occur at the very second
of resume back and it don't follow normal command path, which lead
to bug.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   28 ++++++++++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 3756d54..35149fe 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1246,18 +1246,34 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
+	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
+	for (cs = 0; cs < pdata->num_cs; cs ++) {
+		mtd = info->host[cs]->mtd;
+		mtd->suspend(mtd);
+	}
+
+	clk_disable(info->clk);
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
+	int cs;
+
+	pdata = pdev->dev.platform_data;
+	/* We don't want to handle interrupt without calling mtd routine */
+	disable_int(info, NDCR_INT_MASK);
 
 	/*
 	 * Directly set the chip select to a invalid value,
@@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	info->cs = 0xff;
 	clk_enable(info->clk);
 
+	/*
+	 * As the spec, the NDSR would be updated to 0x1800 when
+	 * do the nand_clk disable/enable.
+	 * To prevent it damage state machine of the driver, clear
+	 * all status before resume
+	 */
+	nand_writel(nand, NDSR, NDSR_MASK);
+	for (cs = 0; cs < pdata->num_cs; cs ++) {
+		mtd = info->host[cs]->mtd;
+		mtd->resume(mtd);
+	}
+
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-12  9:57                             ` Daniel Mack
@ 2011-07-12 10:29                               ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:29 UTC (permalink / raw)
  To: Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, Igor Grinberg, linux-arm-kernel

Hi Daniel,

On Tue, Jul 12, 2011 at 5:57 PM, Daniel Mack <zonque@gmail.com> wrote:
> On Tue, Jul 12, 2011 at 11:40 AM, Lei Wen <adrian.wenl@gmail.com> wrote:
>> I retest with suspend&resume on my board, each time that board would
>> reset the timing in the
>> begining of pxa3xx_cmdfunc as expected. So this should be ok as it is
>> the same behavior
>> with original code.
>
> Ok, just to be sure - as there have been quite some iterations fot
> this now - can you send me a link to your most current version of this
> patch again, please? Just to avoid confusion which version we're
> talking about.
>
>

Please refer to V6 patch set I just send out.

Thanks,
Lei

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-12 10:29                               ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 10:29 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Daniel,

On Tue, Jul 12, 2011 at 5:57 PM, Daniel Mack <zonque@gmail.com> wrote:
> On Tue, Jul 12, 2011 at 11:40 AM, Lei Wen <adrian.wenl@gmail.com> wrote:
>> I retest with suspend&resume on my board, each time that board would
>> reset the timing in the
>> begining of pxa3xx_cmdfunc as expected. So this should be ok as it is
>> the same behavior
>> with original code.
>
> Ok, just to be sure - as there have been quite some iterations fot
> this now - can you send me a link to your most current version of this
> patch again, please? Just to avoid confusion which version we're
> talking about.
>
>

Please refer to V6 patch set I just send out.

Thanks,
Lei

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

* Re: [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
  2011-07-12 10:28                   ` Lei Wen
@ 2011-07-12 11:39                     ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 11:39 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Yu Tang,
	Haojian Zhuang, linux-mtd, Igor Grinberg, linux-arm-kernel

On Tue, Jul 12, 2011 at 12:28 PM, Lei Wen <leiwen@marvell.com> wrote:
> This patch add protection on the suspend&resume path to prevent
> some unexpected behavior, like interrupt occur at the very second
> of resume back and it don't follow normal command path, which lead
> to bug.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
>  drivers/mtd/nand/pxa3xx_nand.c |   28 ++++++++++++++++++++++++++++
>  1 files changed, 28 insertions(+), 0 deletions(-)
>

[...]

> @@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>        info->cs = 0xff;
>        clk_enable(info->clk);
>
> +       /*
> +        * As the spec, the NDSR would be updated to 0x1800 when
> +        * do the nand_clk disable/enable.
> +        * To prevent it damage state machine of the driver, clear
> +        * all status before resume
> +        */
> +       nand_writel(nand, NDSR, NDSR_MASK);

This doesn't build:

  CC      drivers/mtd/nand/pxa3xx_nand.o
drivers/mtd/nand/pxa3xx_nand.c: In function 'pxa3xx_nand_resume':
drivers/mtd/nand/pxa3xx_nand.c:1292: error: 'nand' undeclared (first
use in this function)
drivers/mtd/nand/pxa3xx_nand.c:1292: error: (Each undeclared
identifier is reported only once
drivers/mtd/nand/pxa3xx_nand.c:1292: error: for each function it appears in.)
drivers/mtd/nand/pxa3xx_nand.c:1294: error: 'mtd' undeclared (first
use in this function)
make[3]: *** [drivers/mtd/nand/pxa3xx_nand.o] Error 1

I guess this was not even compile tested? Anyway, I did a trivial
fix-up and will test.


Daniel

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

* [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
@ 2011-07-12 11:39                     ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 11:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 12, 2011 at 12:28 PM, Lei Wen <leiwen@marvell.com> wrote:
> This patch add protection on the suspend&resume path to prevent
> some unexpected behavior, like interrupt occur at the very second
> of resume back and it don't follow normal command path, which lead
> to bug.
>
> Signed-off-by: Lei Wen <leiwen@marvell.com>
> ---
> ?drivers/mtd/nand/pxa3xx_nand.c | ? 28 ++++++++++++++++++++++++++++
> ?1 files changed, 28 insertions(+), 0 deletions(-)
>

[...]

> @@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
> ? ? ? ?info->cs = 0xff;
> ? ? ? ?clk_enable(info->clk);
>
> + ? ? ? /*
> + ? ? ? ?* As the spec, the NDSR would be updated to 0x1800 when
> + ? ? ? ?* do the nand_clk disable/enable.
> + ? ? ? ?* To prevent it damage state machine of the driver, clear
> + ? ? ? ?* all status before resume
> + ? ? ? ?*/
> + ? ? ? nand_writel(nand, NDSR, NDSR_MASK);

This doesn't build:

  CC      drivers/mtd/nand/pxa3xx_nand.o
drivers/mtd/nand/pxa3xx_nand.c: In function 'pxa3xx_nand_resume':
drivers/mtd/nand/pxa3xx_nand.c:1292: error: 'nand' undeclared (first
use in this function)
drivers/mtd/nand/pxa3xx_nand.c:1292: error: (Each undeclared
identifier is reported only once
drivers/mtd/nand/pxa3xx_nand.c:1292: error: for each function it appears in.)
drivers/mtd/nand/pxa3xx_nand.c:1294: error: 'mtd' undeclared (first
use in this function)
make[3]: *** [drivers/mtd/nand/pxa3xx_nand.o] Error 1

I guess this was not even compile tested? Anyway, I did a trivial
fix-up and will test.


Daniel

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

* Re: [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
  2011-07-12 11:39                     ` Daniel Mack
@ 2011-07-12 12:02                       ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 12:02 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Yu Tang,
	Haojian Zhuang, linux-mtd, Igor Grinberg, linux-arm-kernel

On Tue, Jul 12, 2011 at 1:39 PM, Daniel Mack <zonque@gmail.com> wrote:
> On Tue, Jul 12, 2011 at 12:28 PM, Lei Wen <leiwen@marvell.com> wrote:
>> This patch add protection on the suspend&resume path to prevent
>> some unexpected behavior, like interrupt occur at the very second
>> of resume back and it don't follow normal command path, which lead
>> to bug.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>>  drivers/mtd/nand/pxa3xx_nand.c |   28 ++++++++++++++++++++++++++++
>>  1 files changed, 28 insertions(+), 0 deletions(-)
>>
>
> [...]
>
>> @@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>        info->cs = 0xff;
>>        clk_enable(info->clk);
>>
>> +       /*
>> +        * As the spec, the NDSR would be updated to 0x1800 when
>> +        * do the nand_clk disable/enable.
>> +        * To prevent it damage state machine of the driver, clear
>> +        * all status before resume
>> +        */
>> +       nand_writel(nand, NDSR, NDSR_MASK);
>
> This doesn't build:
>
>  CC      drivers/mtd/nand/pxa3xx_nand.o
> drivers/mtd/nand/pxa3xx_nand.c: In function 'pxa3xx_nand_resume':
> drivers/mtd/nand/pxa3xx_nand.c:1292: error: 'nand' undeclared (first
> use in this function)
> drivers/mtd/nand/pxa3xx_nand.c:1292: error: (Each undeclared
> identifier is reported only once
> drivers/mtd/nand/pxa3xx_nand.c:1292: error: for each function it appears in.)
> drivers/mtd/nand/pxa3xx_nand.c:1294: error: 'mtd' undeclared (first
> use in this function)
> make[3]: *** [drivers/mtd/nand/pxa3xx_nand.o] Error 1
>
> I guess this was not even compile tested? Anyway, I did a trivial
> fix-up and will test.

Also, with this (fixed) patch applied, the system doesn't resume at
all. No messages, it simply doesn't come back.


Daniel

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

* [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
@ 2011-07-12 12:02                       ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 12:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 12, 2011 at 1:39 PM, Daniel Mack <zonque@gmail.com> wrote:
> On Tue, Jul 12, 2011 at 12:28 PM, Lei Wen <leiwen@marvell.com> wrote:
>> This patch add protection on the suspend&resume path to prevent
>> some unexpected behavior, like interrupt occur at the very second
>> of resume back and it don't follow normal command path, which lead
>> to bug.
>>
>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>> ---
>> ?drivers/mtd/nand/pxa3xx_nand.c | ? 28 ++++++++++++++++++++++++++++
>> ?1 files changed, 28 insertions(+), 0 deletions(-)
>>
>
> [...]
>
>> @@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>> ? ? ? ?info->cs = 0xff;
>> ? ? ? ?clk_enable(info->clk);
>>
>> + ? ? ? /*
>> + ? ? ? ?* As the spec, the NDSR would be updated to 0x1800 when
>> + ? ? ? ?* do the nand_clk disable/enable.
>> + ? ? ? ?* To prevent it damage state machine of the driver, clear
>> + ? ? ? ?* all status before resume
>> + ? ? ? ?*/
>> + ? ? ? nand_writel(nand, NDSR, NDSR_MASK);
>
> This doesn't build:
>
> ?CC ? ? ?drivers/mtd/nand/pxa3xx_nand.o
> drivers/mtd/nand/pxa3xx_nand.c: In function 'pxa3xx_nand_resume':
> drivers/mtd/nand/pxa3xx_nand.c:1292: error: 'nand' undeclared (first
> use in this function)
> drivers/mtd/nand/pxa3xx_nand.c:1292: error: (Each undeclared
> identifier is reported only once
> drivers/mtd/nand/pxa3xx_nand.c:1292: error: for each function it appears in.)
> drivers/mtd/nand/pxa3xx_nand.c:1294: error: 'mtd' undeclared (first
> use in this function)
> make[3]: *** [drivers/mtd/nand/pxa3xx_nand.o] Error 1
>
> I guess this was not even compile tested? Anyway, I did a trivial
> fix-up and will test.

Also, with this (fixed) patch applied, the system doesn't resume at
all. No messages, it simply doesn't come back.


Daniel

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-12 10:29                               ` Lei Wen
@ 2011-07-12 12:05                                 ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 12:05 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, Igor Grinberg, linux-arm-kernel

On Tue, Jul 12, 2011 at 12:29 PM, Lei Wen <adrian.wenl@gmail.com> wrote:
> Hi Daniel,
>
> On Tue, Jul 12, 2011 at 5:57 PM, Daniel Mack <zonque@gmail.com> wrote:
>> On Tue, Jul 12, 2011 at 11:40 AM, Lei Wen <adrian.wenl@gmail.com> wrote:
>>> I retest with suspend&resume on my board, each time that board would
>>> reset the timing in the
>>> begining of pxa3xx_cmdfunc as expected. So this should be ok as it is
>>> the same behavior
>>> with original code.
>>
>> Ok, just to be sure - as there have been quite some iterations fot
>> this now - can you send me a link to your most current version of this
>> patch again, please? Just to avoid confusion which version we're
>> talking about.
>>
>>
>
> Please refer to V6 patch set I just send out.

I did. With all 4 patches applied, the system doesn't wake up again,
as I posted as reply to this particular patch. With only the first 3
applied, we see the following messages after resume:

[ 1297.173920] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1297.373887] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1297.379014] UBIFS error (pid 1245): ubifs_read_node: bad node type
(255 but expected 2)
[ 1297.388521] UBIFS error (pid 1245): ubifs_read_node: bad node at
LEB 435:102784, LEB mapping status 1
[ 1297.593895] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1297.793884] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1297.994294] pxa3xx-nand pxa3xx-nand: Wait time out!!!



Daniel

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-12 12:05                                 ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 12:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 12, 2011 at 12:29 PM, Lei Wen <adrian.wenl@gmail.com> wrote:
> Hi Daniel,
>
> On Tue, Jul 12, 2011 at 5:57 PM, Daniel Mack <zonque@gmail.com> wrote:
>> On Tue, Jul 12, 2011 at 11:40 AM, Lei Wen <adrian.wenl@gmail.com> wrote:
>>> I retest with suspend&resume on my board, each time that board would
>>> reset the timing in the
>>> begining of pxa3xx_cmdfunc as expected. So this should be ok as it is
>>> the same behavior
>>> with original code.
>>
>> Ok, just to be sure - as there have been quite some iterations fot
>> this now - can you send me a link to your most current version of this
>> patch again, please? Just to avoid confusion which version we're
>> talking about.
>>
>>
>
> Please refer to V6 patch set I just send out.

I did. With all 4 patches applied, the system doesn't wake up again,
as I posted as reply to this particular patch. With only the first 3
applied, we see the following messages after resume:

[ 1297.173920] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1297.373887] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1297.379014] UBIFS error (pid 1245): ubifs_read_node: bad node type
(255 but expected 2)
[ 1297.388521] UBIFS error (pid 1245): ubifs_read_node: bad node at
LEB 435:102784, LEB mapping status 1
[ 1297.593895] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1297.793884] pxa3xx-nand pxa3xx-nand: Wait time out!!!
[ 1297.994294] pxa3xx-nand pxa3xx-nand: Wait time out!!!



Daniel

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-11 19:25                         ` Igor Grinberg
@ 2011-07-12 12:48                           ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 12:48 UTC (permalink / raw)
  To: Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

On Mon, Jul 11, 2011 at 9:25 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/11/11 21:53, Daniel Mack wrote:
>> At least not in our case. The first level bootloader is entered on
>> resume just as it is on a POR event, with the exception that the D3S
>> bit in the ASCR register is set in this case.
>
> You say "first level boot loader is entered"... who is loading it and where from?
> Indeed, it should happen as in POR case, but again, it resides on some non-volatile
> storage, isn't it? What storage does it reside on?

That is true. I was assuming that the first-stage loader remains in
SRAM and is re-used later, but I think you're right, and it is loaded
from NAND again after resume.

>> We unconditionally
>> initialize the static and dynamic memory controllers and then either
>> jump to the routine that initializes the NAND controller, read the 2nd
>> level loader and pass control to it. Or (in the resume case) we just
>> jump to the address stored at the RAM address 0x80000000 (which has
>> been set to cpu_resume previously) and thus enter the kernel again. No
>> NAND operations in the game in this case, and this has always worked
>> fine.
>
> Except those to read the first stage boot loader... (if it resides on NAND).
> Well, our case is working similarly and both the first stage bootloader (OBM)
> and the second stage bootloader (U-Boot) reside on the NAND flash,
> therefore BootROM has to reconfigure the NAND flash, so it can boot (resume).

Well, the load of the first NAND page (where the OBM resides) is out
of control for our software, as the code in PXA's internal ROM mask
takes care for this. We do not, however, re-initialize the NAND
controller in the resume case, just as we don't restore any other
pheripheral's registers. And this has worked fine all the time, and it
still does if Lei's latest patch set is not applied.


Daniel

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-12 12:48                           ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 12:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jul 11, 2011 at 9:25 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/11/11 21:53, Daniel Mack wrote:
>> At least not in our case. The first level bootloader is entered on
>> resume just as it is on a POR event, with the exception that the D3S
>> bit in the ASCR register is set in this case.
>
> You say "first level boot loader is entered"... who is loading it and where from?
> Indeed, it should happen as in POR case, but again, it resides on some non-volatile
> storage, isn't it? What storage does it reside on?

That is true. I was assuming that the first-stage loader remains in
SRAM and is re-used later, but I think you're right, and it is loaded
from NAND again after resume.

>> We unconditionally
>> initialize the static and dynamic memory controllers and then either
>> jump to the routine that initializes the NAND controller, read the 2nd
>> level loader and pass control to it. Or (in the resume case) we just
>> jump to the address stored at the RAM address 0x80000000 (which has
>> been set to cpu_resume previously) and thus enter the kernel again. No
>> NAND operations in the game in this case, and this has always worked
>> fine.
>
> Except those to read the first stage boot loader... (if it resides on NAND).
> Well, our case is working similarly and both the first stage bootloader (OBM)
> and the second stage bootloader (U-Boot) reside on the NAND flash,
> therefore BootROM has to reconfigure the NAND flash, so it can boot (resume).

Well, the load of the first NAND page (where the OBM resides) is out
of control for our software, as the code in PXA's internal ROM mask
takes care for this. We do not, however, re-initialize the NAND
controller in the resume case, just as we don't restore any other
pheripheral's registers. And this has worked fine all the time, and it
still does if Lei's latest patch set is not applied.


Daniel

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

* [PATCH V7 0/4] pxa3xx_nand: add two chip select support
  2011-07-12 10:28                   ` Lei Wen
@ 2011-07-12 14:35                     ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

V5:
Merge platform change patch and two chip feature into one patch.
Clean some format issue.

V6:
Add suspend and resume enhancement as a part of this patch set.

V7:
Fix build bug
Fix zylonite machine cannot resume back

Lei Wen (4):
  MTD: pxa3xx_nand: enhance suspend and resume routine
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  453 ++++++++++++++++----------
 9 files changed, 327 insertions(+), 185 deletions(-)

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

* [PATCH V7 0/4] pxa3xx_nand: add two chip select support
@ 2011-07-12 14:35                     ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: linux-arm-kernel

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

V5:
Merge platform change patch and two chip feature into one patch.
Clean some format issue.

V6:
Add suspend and resume enhancement as a part of this patch set.

V7:
Fix build bug
Fix zylonite machine cannot resume back

Lei Wen (4):
  MTD: pxa3xx_nand: enhance suspend and resume routine
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  453 ++++++++++++++++----------
 9 files changed, 327 insertions(+), 185 deletions(-)

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

* [PATCH V7 1/4] MTD: pxa3xx_nand: enhance suspend and resume routine
  2011-07-12 10:28                   ` Lei Wen
@ 2011-07-12 14:35                     ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

This patch add protection on the suspend&resume path to prevent
some unexpected behavior, like interrupt occur at the very second
of resume back and it don't follow normal command path, which lead
to bug.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   17 ++++++++++++++++-
 1 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..1efecf7 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1158,23 +1158,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct mtd_info *mtd;
 
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
+	mtd = info->mtd;
+	mtd->suspend(mtd);
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct mtd_info *mtd;
+
+	/* We don't want to handle interrupt without calling mtd routine */
+	disable_int(info, NDCR_INT_MASK);
 
 	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
 	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
-	clk_enable(info->clk);
 
+	/*
+	 * As the spec, the NDSR would be updated to 0x1800 when
+	 * do the nand_clk disable/enable.
+	 * To prevent it damage state machine of the driver, clear
+	 * all status before resume
+	 */
+	nand_writel(info, NDSR, NDSR_MASK);
+	mtd = info->mtd;
+	mtd->resume(mtd);
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* [PATCH V7 1/4] MTD: pxa3xx_nand: enhance suspend and resume routine
@ 2011-07-12 14:35                     ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: linux-arm-kernel

This patch add protection on the suspend&resume path to prevent
some unexpected behavior, like interrupt occur at the very second
of resume back and it don't follow normal command path, which lead
to bug.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   17 ++++++++++++++++-
 1 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..1efecf7 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1158,23 +1158,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct mtd_info *mtd;
 
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
+	mtd = info->mtd;
+	mtd->suspend(mtd);
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct mtd_info *mtd;
+
+	/* We don't want to handle interrupt without calling mtd routine */
+	disable_int(info, NDCR_INT_MASK);
 
 	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
 	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
-	clk_enable(info->clk);
 
+	/*
+	 * As the spec, the NDSR would be updated to 0x1800 when
+	 * do the nand_clk disable/enable.
+	 * To prevent it damage state machine of the driver, clear
+	 * all status before resume
+	 */
+	nand_writel(info, NDSR, NDSR_MASK);
+	mtd = info->mtd;
+	mtd->resume(mtd);
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-07-12 10:28                   ` Lei Wen
@ 2011-07-12 14:35                     ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 1efecf7..afe14bd 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-07-12 14:35                     ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 1efecf7..afe14bd 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev, "Read out ID 0, "
+				"potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH V7 3/4] MTD: pxa3xx_nand: sperate each chip individual info
  2011-07-12 10:28                   ` Lei Wen
@ 2011-07-12 14:35                     ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  292 ++++++++++++++++++++++------------------
 1 files changed, 160 insertions(+), 132 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index afe14bd..3b8bb38 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
 
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,52 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +867,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +874,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +884,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +916,10 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +927,6 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -933,14 +946,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -952,47 +968,56 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	pxa3xx_flash_ids[1].name = NULL;
 	def = pxa3xx_flash_ids;
 KEEP_CONFIG:
+	chip->ecc.mode = NAND_ECC_HW;
+	chip->ecc.size = host->page_size;
+
+	chip->options = NAND_NO_AUTOINCR;
+	chip->options |= NAND_NO_READRDY;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
+
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
-	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
-
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
-	chip->options |= NAND_NO_AUTOINCR;
-	chip->options |= NAND_NO_READRDY;
+		host->row_addr_cycles = 2;
 
+	mtd->name = mtd_names[0];
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1025,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1103,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1121,19 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
+
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1141,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1153,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1162,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1170,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1183,8 +1211,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	/* We don't want to handle interrupt without calling mtd routine */
 	disable_int(info, NDCR_INT_MASK);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 
 	/*
 	 * As the spec, the NDSR would be updated to 0x1800 when
-- 
1.7.0.4

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

* [PATCH V7 3/4] MTD: pxa3xx_nand: sperate each chip individual info
@ 2011-07-12 14:35                     ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: linux-arm-kernel

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  292 ++++++++++++++++++++++------------------
 1 files changed, 160 insertions(+), 132 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index afe14bd..3b8bb38 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
+
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
 
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,52 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +867,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +874,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +884,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +916,10 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +927,6 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_warn(&info->pdev->dev, "Read out ID 0, "
 				"potential timing set wrong!!\n");
 
@@ -933,14 +946,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -952,47 +968,56 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	pxa3xx_flash_ids[1].name = NULL;
 	def = pxa3xx_flash_ids;
 KEEP_CONFIG:
+	chip->ecc.mode = NAND_ECC_HW;
+	chip->ecc.size = host->page_size;
+
+	chip->options = NAND_NO_AUTOINCR;
+	chip->options |= NAND_NO_READRDY;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
+
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
-	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
-
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
-	chip->options |= NAND_NO_AUTOINCR;
-	chip->options |= NAND_NO_READRDY;
+		host->row_addr_cycles = 2;
 
+	mtd->name = mtd_names[0];
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1025,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1103,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1121,19 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
+
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1141,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1153,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1162,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1170,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1183,8 +1211,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	/* We don't want to handle interrupt without calling mtd routine */
 	disable_int(info, NDCR_INT_MASK);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 
 	/*
 	 * As the spec, the NDSR would be updated to 0x1800 when
-- 
1.7.0.4

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

* [PATCH V7 4/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-12 10:28                   ` Lei Wen
@ 2011-07-12 14:35                     ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  171 ++++++++++++++++++--------
 9 files changed, 162 insertions(+), 68 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 3b8bb38..3692eb6 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -884,9 +915,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -917,7 +948,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
+			 info->cs);
 
 		return ret;
 	}
@@ -996,41 +1028,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1128,12 +1166,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1153,7 +1193,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1162,7 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1177,42 +1218,69 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
-	mtd = info->mtd;
-	mtd->suspend(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs ++) {
+		mtd = info->host[cs]->mtd;
+		mtd->suspend(mtd);
+	}
+
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	/* We don't want to handle interrupt without calling mtd routine */
 	disable_int(info, NDCR_INT_MASK);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 
 	/*
 	 * As the spec, the NDSR would be updated to 0x1800 when
@@ -1221,8 +1289,11 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	 * all status before resume
 	 */
 	nand_writel(info, NDSR, NDSR_MASK);
-	mtd = info->mtd;
-	mtd->resume(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs ++) {
+		mtd = info->host[cs]->mtd;
+		mtd->resume(mtd);
+	}
+
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* [PATCH V7 4/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-12 14:35                     ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-12 14:35 UTC (permalink / raw)
  To: linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  171 ++++++++++++++++++--------
 9 files changed, 162 insertions(+), 68 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 3b8bb38..3692eb6 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -884,9 +915,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -917,7 +948,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
+			 info->cs);
 
 		return ret;
 	}
@@ -996,41 +1028,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1128,12 +1166,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1153,7 +1193,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1162,7 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1177,42 +1218,69 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
-	mtd = info->mtd;
-	mtd->suspend(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs ++) {
+		mtd = info->host[cs]->mtd;
+		mtd->suspend(mtd);
+	}
+
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	/* We don't want to handle interrupt without calling mtd routine */
 	disable_int(info, NDCR_INT_MASK);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 
 	/*
 	 * As the spec, the NDSR would be updated to 0x1800 when
@@ -1221,8 +1289,11 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	 * all status before resume
 	 */
 	nand_writel(info, NDSR, NDSR_MASK);
-	mtd = info->mtd;
-	mtd->resume(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs ++) {
+		mtd = info->host[cs]->mtd;
+		mtd->resume(mtd);
+	}
+
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* Re: [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-12 12:48                           ` Daniel Mack
@ 2011-07-12 15:49                             ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-12 15:49 UTC (permalink / raw)
  To: Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

On 07/12/11 15:48, Daniel Mack wrote:

> On Mon, Jul 11, 2011 at 9:25 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 07/11/11 21:53, Daniel Mack wrote:
>>> At least not in our case. The first level bootloader is entered on
>>> resume just as it is on a POR event, with the exception that the D3S
>>> bit in the ASCR register is set in this case.
>> You say "first level boot loader is entered"... who is loading it and where from?
>> Indeed, it should happen as in POR case, but again, it resides on some non-volatile
>> storage, isn't it? What storage does it reside on?
> That is true. I was assuming that the first-stage loader remains in
> SRAM and is re-used later, but I think you're right, and it is loaded
> from NAND again after resume.
>
>>> We unconditionally
>>> initialize the static and dynamic memory controllers and then either
>>> jump to the routine that initializes the NAND controller, read the 2nd
>>> level loader and pass control to it. Or (in the resume case) we just
>>> jump to the address stored at the RAM address 0x80000000 (which has
>>> been set to cpu_resume previously) and thus enter the kernel again. No
>>> NAND operations in the game in this case, and this has always worked
>>> fine.
>> Except those to read the first stage boot loader... (if it resides on NAND).
>> Well, our case is working similarly and both the first stage bootloader (OBM)
>> and the second stage bootloader (U-Boot) reside on the NAND flash,
>> therefore BootROM has to reconfigure the NAND flash, so it can boot (resume).
> Well, the load of the first NAND page (where the OBM resides)

You mean block, page is too small for the OBM to fit...

>  is out
> of control for our software, as the code in PXA's internal ROM mask
> takes care for this.

Exactly... the BootROM is configuring the NAND controller
and (I think) that is one of the reasons we have the keep_config in first place...

> We do not, however, re-initialize the NAND
> controller in the resume case, just as we don't restore any other
> pheripheral's registers.

Yes, the NAND initialization is done by the BootROM, so you don't need to reinitialize it.
Just the DRAM controller (may be SRAM also), so you will be able to jump back to Linux.

> And this has worked fine all the time, and it
> still does if Lei's latest patch set is not applied.


-- 
Regards,
Igor.

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

* [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-12 15:49                             ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-12 15:49 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/12/11 15:48, Daniel Mack wrote:

> On Mon, Jul 11, 2011 at 9:25 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
>> On 07/11/11 21:53, Daniel Mack wrote:
>>> At least not in our case. The first level bootloader is entered on
>>> resume just as it is on a POR event, with the exception that the D3S
>>> bit in the ASCR register is set in this case.
>> You say "first level boot loader is entered"... who is loading it and where from?
>> Indeed, it should happen as in POR case, but again, it resides on some non-volatile
>> storage, isn't it? What storage does it reside on?
> That is true. I was assuming that the first-stage loader remains in
> SRAM and is re-used later, but I think you're right, and it is loaded
> from NAND again after resume.
>
>>> We unconditionally
>>> initialize the static and dynamic memory controllers and then either
>>> jump to the routine that initializes the NAND controller, read the 2nd
>>> level loader and pass control to it. Or (in the resume case) we just
>>> jump to the address stored at the RAM address 0x80000000 (which has
>>> been set to cpu_resume previously) and thus enter the kernel again. No
>>> NAND operations in the game in this case, and this has always worked
>>> fine.
>> Except those to read the first stage boot loader... (if it resides on NAND).
>> Well, our case is working similarly and both the first stage bootloader (OBM)
>> and the second stage bootloader (U-Boot) reside on the NAND flash,
>> therefore BootROM has to reconfigure the NAND flash, so it can boot (resume).
> Well, the load of the first NAND page (where the OBM resides)

You mean block, page is too small for the OBM to fit...

>  is out
> of control for our software, as the code in PXA's internal ROM mask
> takes care for this.

Exactly... the BootROM is configuring the NAND controller
and (I think) that is one of the reasons we have the keep_config in first place...

> We do not, however, re-initialize the NAND
> controller in the resume case, just as we don't restore any other
> pheripheral's registers.

Yes, the NAND initialization is done by the BootROM, so you don't need to reinitialize it.
Just the DRAM controller (may be SRAM also), so you will be able to jump back to Linux.

> And this has worked fine all the time, and it
> still does if Lei's latest patch set is not applied.


-- 
Regards,
Igor.

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

* Re: [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
  2011-07-12 12:02                       ` Daniel Mack
@ 2011-07-12 15:56                         ` Igor Grinberg
  -1 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-12 15:56 UTC (permalink / raw)
  To: Daniel Mack
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen, Yu Tang,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

On 07/12/11 15:02, Daniel Mack wrote:

> On Tue, Jul 12, 2011 at 1:39 PM, Daniel Mack <zonque@gmail.com> wrote:
>> On Tue, Jul 12, 2011 at 12:28 PM, Lei Wen <leiwen@marvell.com> wrote:
>>> This patch add protection on the suspend&resume path to prevent
>>> some unexpected behavior, like interrupt occur at the very second
>>> of resume back and it don't follow normal command path, which lead
>>> to bug.
>>>
>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>> ---
>>>  drivers/mtd/nand/pxa3xx_nand.c |   28 ++++++++++++++++++++++++++++
>>>  1 files changed, 28 insertions(+), 0 deletions(-)
>>>
>> [...]
>>
>>> @@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>        info->cs = 0xff;
>>>        clk_enable(info->clk);
>>>
>>> +       /*
>>> +        * As the spec, the NDSR would be updated to 0x1800 when
>>> +        * do the nand_clk disable/enable.
>>> +        * To prevent it damage state machine of the driver, clear
>>> +        * all status before resume
>>> +        */
>>> +       nand_writel(nand, NDSR, NDSR_MASK);
>> This doesn't build:
>>
>>  CC      drivers/mtd/nand/pxa3xx_nand.o
>> drivers/mtd/nand/pxa3xx_nand.c: In function 'pxa3xx_nand_resume':
>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: 'nand' undeclared (first
>> use in this function)
>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: (Each undeclared
>> identifier is reported only once
>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: for each function it appears in.)
>> drivers/mtd/nand/pxa3xx_nand.c:1294: error: 'mtd' undeclared (first
>> use in this function)
>> make[3]: *** [drivers/mtd/nand/pxa3xx_nand.o] Error 1
>>
>> I guess this was not even compile tested? Anyway, I did a trivial
>> fix-up and will test.
> Also, with this (fixed) patch applied, the system doesn't resume at
> all. No messages, it simply doesn't come back.

I was skeptic about the clock being disabled in Lei's patch,
as I observed system hangs if that clock was disabled back then in 2.6.31,
but wanted to give it a try, because things has changed since then.

Now I see, that Lei already sent v7 without clock toggling...



-- 
Regards,
Igor.

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

* [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
@ 2011-07-12 15:56                         ` Igor Grinberg
  0 siblings, 0 replies; 162+ messages in thread
From: Igor Grinberg @ 2011-07-12 15:56 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/12/11 15:02, Daniel Mack wrote:

> On Tue, Jul 12, 2011 at 1:39 PM, Daniel Mack <zonque@gmail.com> wrote:
>> On Tue, Jul 12, 2011 at 12:28 PM, Lei Wen <leiwen@marvell.com> wrote:
>>> This patch add protection on the suspend&resume path to prevent
>>> some unexpected behavior, like interrupt occur at the very second
>>> of resume back and it don't follow normal command path, which lead
>>> to bug.
>>>
>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>> ---
>>>  drivers/mtd/nand/pxa3xx_nand.c |   28 ++++++++++++++++++++++++++++
>>>  1 files changed, 28 insertions(+), 0 deletions(-)
>>>
>> [...]
>>
>>> @@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>        info->cs = 0xff;
>>>        clk_enable(info->clk);
>>>
>>> +       /*
>>> +        * As the spec, the NDSR would be updated to 0x1800 when
>>> +        * do the nand_clk disable/enable.
>>> +        * To prevent it damage state machine of the driver, clear
>>> +        * all status before resume
>>> +        */
>>> +       nand_writel(nand, NDSR, NDSR_MASK);
>> This doesn't build:
>>
>>  CC      drivers/mtd/nand/pxa3xx_nand.o
>> drivers/mtd/nand/pxa3xx_nand.c: In function 'pxa3xx_nand_resume':
>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: 'nand' undeclared (first
>> use in this function)
>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: (Each undeclared
>> identifier is reported only once
>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: for each function it appears in.)
>> drivers/mtd/nand/pxa3xx_nand.c:1294: error: 'mtd' undeclared (first
>> use in this function)
>> make[3]: *** [drivers/mtd/nand/pxa3xx_nand.o] Error 1
>>
>> I guess this was not even compile tested? Anyway, I did a trivial
>> fix-up and will test.
> Also, with this (fixed) patch applied, the system doesn't resume at
> all. No messages, it simply doesn't come back.

I was skeptic about the clock being disabled in Lei's patch,
as I observed system hangs if that clock was disabled back then in 2.6.31,
but wanted to give it a try, because things has changed since then.

Now I see, that Lei already sent v7 without clock toggling...



-- 
Regards,
Igor.

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

* Re: [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
  2011-07-12 15:56                         ` Igor Grinberg
@ 2011-07-12 17:35                           ` Daniel Mack
  -1 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 17:35 UTC (permalink / raw)
  To: Igor Grinberg
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen, Yu Tang,
	Haojian Zhuang, linux-mtd, linux-arm-kernel

On Tue, Jul 12, 2011 at 5:56 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/12/11 15:02, Daniel Mack wrote:
>
>> On Tue, Jul 12, 2011 at 1:39 PM, Daniel Mack <zonque@gmail.com> wrote:
>>> On Tue, Jul 12, 2011 at 12:28 PM, Lei Wen <leiwen@marvell.com> wrote:
>>>> This patch add protection on the suspend&resume path to prevent
>>>> some unexpected behavior, like interrupt occur at the very second
>>>> of resume back and it don't follow normal command path, which lead
>>>> to bug.
>>>>
>>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>>> ---
>>>>  drivers/mtd/nand/pxa3xx_nand.c |   28 ++++++++++++++++++++++++++++
>>>>  1 files changed, 28 insertions(+), 0 deletions(-)
>>>>
>>> [...]
>>>
>>>> @@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>>        info->cs = 0xff;
>>>>        clk_enable(info->clk);
>>>>
>>>> +       /*
>>>> +        * As the spec, the NDSR would be updated to 0x1800 when
>>>> +        * do the nand_clk disable/enable.
>>>> +        * To prevent it damage state machine of the driver, clear
>>>> +        * all status before resume
>>>> +        */
>>>> +       nand_writel(nand, NDSR, NDSR_MASK);
>>> This doesn't build:
>>>
>>>  CC      drivers/mtd/nand/pxa3xx_nand.o
>>> drivers/mtd/nand/pxa3xx_nand.c: In function 'pxa3xx_nand_resume':
>>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: 'nand' undeclared (first
>>> use in this function)
>>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: (Each undeclared
>>> identifier is reported only once
>>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: for each function it appears in.)
>>> drivers/mtd/nand/pxa3xx_nand.c:1294: error: 'mtd' undeclared (first
>>> use in this function)
>>> make[3]: *** [drivers/mtd/nand/pxa3xx_nand.o] Error 1
>>>
>>> I guess this was not even compile tested? Anyway, I did a trivial
>>> fix-up and will test.
>> Also, with this (fixed) patch applied, the system doesn't resume at
>> all. No messages, it simply doesn't come back.
>
> I was skeptic about the clock being disabled in Lei's patch,
> as I observed system hangs if that clock was disabled back then in 2.6.31,
> but wanted to give it a try, because things has changed since then.
>
> Now I see, that Lei already sent v7 without clock toggling...

Yes, we debugged this quickly via Jabber, and without the clock
disable, things work fine for me again.

Daniel

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

* [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine
@ 2011-07-12 17:35                           ` Daniel Mack
  0 siblings, 0 replies; 162+ messages in thread
From: Daniel Mack @ 2011-07-12 17:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 12, 2011 at 5:56 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> On 07/12/11 15:02, Daniel Mack wrote:
>
>> On Tue, Jul 12, 2011 at 1:39 PM, Daniel Mack <zonque@gmail.com> wrote:
>>> On Tue, Jul 12, 2011 at 12:28 PM, Lei Wen <leiwen@marvell.com> wrote:
>>>> This patch add protection on the suspend&resume path to prevent
>>>> some unexpected behavior, like interrupt occur at the very second
>>>> of resume back and it don't follow normal command path, which lead
>>>> to bug.
>>>>
>>>> Signed-off-by: Lei Wen <leiwen@marvell.com>
>>>> ---
>>>> ?drivers/mtd/nand/pxa3xx_nand.c | ? 28 ++++++++++++++++++++++++++++
>>>> ?1 files changed, 28 insertions(+), 0 deletions(-)
>>>>
>>> [...]
>>>
>>>> @@ -1267,6 +1283,18 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
>>>> ? ? ? ?info->cs = 0xff;
>>>> ? ? ? ?clk_enable(info->clk);
>>>>
>>>> + ? ? ? /*
>>>> + ? ? ? ?* As the spec, the NDSR would be updated to 0x1800 when
>>>> + ? ? ? ?* do the nand_clk disable/enable.
>>>> + ? ? ? ?* To prevent it damage state machine of the driver, clear
>>>> + ? ? ? ?* all status before resume
>>>> + ? ? ? ?*/
>>>> + ? ? ? nand_writel(nand, NDSR, NDSR_MASK);
>>> This doesn't build:
>>>
>>> ?CC ? ? ?drivers/mtd/nand/pxa3xx_nand.o
>>> drivers/mtd/nand/pxa3xx_nand.c: In function 'pxa3xx_nand_resume':
>>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: 'nand' undeclared (first
>>> use in this function)
>>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: (Each undeclared
>>> identifier is reported only once
>>> drivers/mtd/nand/pxa3xx_nand.c:1292: error: for each function it appears in.)
>>> drivers/mtd/nand/pxa3xx_nand.c:1294: error: 'mtd' undeclared (first
>>> use in this function)
>>> make[3]: *** [drivers/mtd/nand/pxa3xx_nand.o] Error 1
>>>
>>> I guess this was not even compile tested? Anyway, I did a trivial
>>> fix-up and will test.
>> Also, with this (fixed) patch applied, the system doesn't resume at
>> all. No messages, it simply doesn't come back.
>
> I was skeptic about the clock being disabled in Lei's patch,
> as I observed system hangs if that clock was disabled back then in 2.6.31,
> but wanted to give it a try, because things has changed since then.
>
> Now I see, that Lei already sent v7 without clock toggling...

Yes, we debugged this quickly via Jabber, and without the clock
disable, things work fine for me again.

Daniel

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

* Re: [PATCH V7 1/4] MTD: pxa3xx_nand: enhance suspend and resume routine
  2011-07-12 14:35                     ` Lei Wen
@ 2011-07-13 10:53                       ` Sergei Shtylyov
  -1 siblings, 0 replies; 162+ messages in thread
From: Sergei Shtylyov @ 2011-07-13 10:53 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Yu Tang,
	Haojian Zhuang, Daniel Mack, linux-mtd, Igor Grinberg,
	linux-arm-kernel

Hello.

On 12-07-2011 18:35, Lei Wen wrote:

> This patch add protection on the suspend&resume path to prevent
> some unexpected behavior, like interrupt occur at the very second
> of resume back and it don't follow normal command path, which lead
> to bug.

> Signed-off-by: Lei Wen<leiwen@marvell.com>
> ---
>   drivers/mtd/nand/pxa3xx_nand.c |   17 ++++++++++++++++-
>   1 files changed, 16 insertions(+), 1 deletions(-)

> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index b7db1b2..1efecf7 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -1158,23 +1158,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
>   static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
>   {
>   	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> +	struct mtd_info *mtd;
>
>   	if (info->state) {
>   		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
>   		return -EAGAIN;
>   	}
>
> +	mtd = info->mtd;

    Why not make this into an initializer?

> +	mtd->suspend(mtd);
>   	return 0;
>   }
>
>   static int pxa3xx_nand_resume(struct platform_device *pdev)
>   {
>   	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> +	struct mtd_info *mtd;
> +
> +	/* We don't want to handle interrupt without calling mtd routine */
> +	disable_int(info, NDCR_INT_MASK);
>
>   	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>   	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
> -	clk_enable(info->clk);
>
> +	/*
> +	 * As the spec, the NDSR would be updated to 0x1800 when

    As the spec says?

> +	 * do the nand_clk disable/enable.

    Doing.

> +	 * To prevent it damage state machine of the driver, clear

    From damaging?

> +	 * all status before resume
> +	 */
> +	nand_writel(info, NDSR, NDSR_MASK);
> +	mtd = info->mtd;

    This one too could be an initializer...

> +	mtd->resume(mtd);
>   	return 0;
>   }
>   #else

WBR, Sergei

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

* [PATCH V7 1/4] MTD: pxa3xx_nand: enhance suspend and resume routine
@ 2011-07-13 10:53                       ` Sergei Shtylyov
  0 siblings, 0 replies; 162+ messages in thread
From: Sergei Shtylyov @ 2011-07-13 10:53 UTC (permalink / raw)
  To: linux-arm-kernel

Hello.

On 12-07-2011 18:35, Lei Wen wrote:

> This patch add protection on the suspend&resume path to prevent
> some unexpected behavior, like interrupt occur at the very second
> of resume back and it don't follow normal command path, which lead
> to bug.

> Signed-off-by: Lei Wen<leiwen@marvell.com>
> ---
>   drivers/mtd/nand/pxa3xx_nand.c |   17 ++++++++++++++++-
>   1 files changed, 16 insertions(+), 1 deletions(-)

> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index b7db1b2..1efecf7 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -1158,23 +1158,38 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
>   static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
>   {
>   	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> +	struct mtd_info *mtd;
>
>   	if (info->state) {
>   		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
>   		return -EAGAIN;
>   	}
>
> +	mtd = info->mtd;

    Why not make this into an initializer?

> +	mtd->suspend(mtd);
>   	return 0;
>   }
>
>   static int pxa3xx_nand_resume(struct platform_device *pdev)
>   {
>   	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
> +	struct mtd_info *mtd;
> +
> +	/* We don't want to handle interrupt without calling mtd routine */
> +	disable_int(info, NDCR_INT_MASK);
>
>   	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
>   	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
> -	clk_enable(info->clk);
>
> +	/*
> +	 * As the spec, the NDSR would be updated to 0x1800 when

    As the spec says?

> +	 * do the nand_clk disable/enable.

    Doing.

> +	 * To prevent it damage state machine of the driver, clear

    From damaging?

> +	 * all status before resume
> +	 */
> +	nand_writel(info, NDSR, NDSR_MASK);
> +	mtd = info->mtd;

    This one too could be an initializer...

> +	mtd->resume(mtd);
>   	return 0;
>   }
>   #else

WBR, Sergei

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

* Re: [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-07-12 14:35                     ` Lei Wen
@ 2011-07-13 10:57                       ` Sergei Shtylyov
  -1 siblings, 0 replies; 162+ messages in thread
From: Sergei Shtylyov @ 2011-07-13 10:57 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Yu Tang,
	Haojian Zhuang, Daniel Mack, linux-mtd, Igor Grinberg,
	linux-arm-kernel

Hello.

On 12-07-2011 18:35, Lei Wen wrote:

> Signed-off-by: Lei Wen<leiwen@marvell.com>
> ---
>   drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
>   1 files changed, 16 insertions(+), 11 deletions(-)

> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index 1efecf7..afe14bd 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
[...]
> @@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>   	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>   	uint32_t ndcr = 0x0; /* enable all interrupts */
>
> -	if (f->page_size != 2048 && f->page_size != 512)
> +	if (f->page_size != 2048 && f->page_size != 512) {
> +		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
>   		return -EINVAL;
> +	}
>
> -	if (f->flash_width != 16 && f->flash_width != 8)
> +	if (f->flash_width != 16 && f->flash_width != 8) {
> +		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");

    So this patch not only converts printk() to dev_*() calls, it also adds 
some dev_err() calls? This should have been mentioned in the change long at 
least...

>   		return -EINVAL;
> +	}
>
>   	/* calculate flash information */
>   	info->cmdset =&default_cmdset;
> @@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>   	if (!ret) {
>   		kfree(mtd);
>   		info->mtd = NULL;
> -		printk(KERN_INFO "There is no nand chip on cs 0!\n");
> +		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
>
>   		return -EINVAL;
>   	}
> @@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>   	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>   	id = *((uint16_t *)(info->data_buff));
>   	if (id != 0)
> -		printk(KERN_INFO "Detect a flash id %x\n", id);
> +		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
>   	else {
>   		kfree(mtd);
>   		info->mtd = NULL;
> -		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
> +		dev_warn(&info->pdev->dev, "Read out ID 0, "
> +				"potential timing set wrong!!\n");

    Don't break the string -- this prevents grepping for it.

WBR, Sergei

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

* [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-07-13 10:57                       ` Sergei Shtylyov
  0 siblings, 0 replies; 162+ messages in thread
From: Sergei Shtylyov @ 2011-07-13 10:57 UTC (permalink / raw)
  To: linux-arm-kernel

Hello.

On 12-07-2011 18:35, Lei Wen wrote:

> Signed-off-by: Lei Wen<leiwen@marvell.com>
> ---
>   drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
>   1 files changed, 16 insertions(+), 11 deletions(-)

> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index 1efecf7..afe14bd 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
[...]
> @@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
>   	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>   	uint32_t ndcr = 0x0; /* enable all interrupts */
>
> -	if (f->page_size != 2048 && f->page_size != 512)
> +	if (f->page_size != 2048 && f->page_size != 512) {
> +		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
>   		return -EINVAL;
> +	}
>
> -	if (f->flash_width != 16 && f->flash_width != 8)
> +	if (f->flash_width != 16 && f->flash_width != 8) {
> +		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");

    So this patch not only converts printk() to dev_*() calls, it also adds 
some dev_err() calls? This should have been mentioned in the change long at 
least...

>   		return -EINVAL;
> +	}
>
>   	/* calculate flash information */
>   	info->cmdset =&default_cmdset;
> @@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>   	if (!ret) {
>   		kfree(mtd);
>   		info->mtd = NULL;
> -		printk(KERN_INFO "There is no nand chip on cs 0!\n");
> +		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
>
>   		return -EINVAL;
>   	}
> @@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>   	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>   	id = *((uint16_t *)(info->data_buff));
>   	if (id != 0)
> -		printk(KERN_INFO "Detect a flash id %x\n", id);
> +		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
>   	else {
>   		kfree(mtd);
>   		info->mtd = NULL;
> -		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
> +		dev_warn(&info->pdev->dev, "Read out ID 0, "
> +				"potential timing set wrong!!\n");

    Don't break the string -- this prevents grepping for it.

WBR, Sergei

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

* Re: [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-07-13 10:57                       ` Sergei Shtylyov
@ 2011-07-13 12:41                         ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-13 12:41 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: Eric Miao, David Woodhouse, Artem Bityutskiy, Lei Wen, Yu Tang,
	Haojian Zhuang, Daniel Mack, linux-mtd, Igor Grinberg,
	linux-arm-kernel

Hi Sergei,

On Wed, Jul 13, 2011 at 6:57 PM, Sergei Shtylyov <sshtylyov@mvista.com> wrote:
> Hello.
>
> On 12-07-2011 18:35, Lei Wen wrote:
>
>> Signed-off-by: Lei Wen<leiwen@marvell.com>
>> ---
>>  drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
>>  1 files changed, 16 insertions(+), 11 deletions(-)
>
>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c
>> b/drivers/mtd/nand/pxa3xx_nand.c
>> index 1efecf7..afe14bd 100644
>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>> +++ b/drivers/mtd/nand/pxa3xx_nand.c
>
> [...]
>>
>> @@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct
>> pxa3xx_nand_info *info,
>>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>        uint32_t ndcr = 0x0; /* enable all interrupts */
>>
>> -       if (f->page_size != 2048 && f->page_size != 512)
>> +       if (f->page_size != 2048 && f->page_size != 512) {
>> +               dev_err(&pdev->dev, "Current only support 2048 and 512
>> size\n");
>>                return -EINVAL;
>> +       }
>>
>> -       if (f->flash_width != 16 && f->flash_width != 8)
>> +       if (f->flash_width != 16 && f->flash_width != 8) {
>> +               dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
>
>   So this patch not only converts printk() to dev_*() calls, it also adds
> some dev_err() calls? This should have been mentioned in the change long at
> least...

What do you mean by "chang long"? Do you mean the cover letter or the
patch name?
Certainly it is a good suggest to show it in the change version list.

>
>>                return -EINVAL;
>> +       }
>>
>>        /* calculate flash information */
>>        info->cmdset =&default_cmdset;
>> @@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>        if (!ret) {
>>                kfree(mtd);
>>                info->mtd = NULL;
>> -               printk(KERN_INFO "There is no nand chip on cs 0!\n");
>> +               dev_info(&info->pdev->dev, "There is no nand chip on cs
>> 0!\n");
>>
>>                return -EINVAL;
>>        }
>> @@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>>        chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>>        id = *((uint16_t *)(info->data_buff));
>>        if (id != 0)
>> -               printk(KERN_INFO "Detect a flash id %x\n", id);
>> +               dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
>>        else {
>>                kfree(mtd);
>>                info->mtd = NULL;
>> -               printk(KERN_WARNING "Read out ID 0, potential timing set
>> wrong!!\n");
>> +               dev_warn(&info->pdev->dev, "Read out ID 0, "
>> +                               "potential timing set wrong!!\n");
>
>   Don't break the string -- this prevents grepping for it.

Thanks, I would fix it.

Best regards,
Lei

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

* [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-07-13 12:41                         ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-13 12:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Sergei,

On Wed, Jul 13, 2011 at 6:57 PM, Sergei Shtylyov <sshtylyov@mvista.com> wrote:
> Hello.
>
> On 12-07-2011 18:35, Lei Wen wrote:
>
>> Signed-off-by: Lei Wen<leiwen@marvell.com>
>> ---
>> ?drivers/mtd/nand/pxa3xx_nand.c | ? 27 ++++++++++++++++-----------
>> ?1 files changed, 16 insertions(+), 11 deletions(-)
>
>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c
>> b/drivers/mtd/nand/pxa3xx_nand.c
>> index 1efecf7..afe14bd 100644
>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>> +++ b/drivers/mtd/nand/pxa3xx_nand.c
>
> [...]
>>
>> @@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct
>> pxa3xx_nand_info *info,
>> ? ? ? ?struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>> ? ? ? ?uint32_t ndcr = 0x0; /* enable all interrupts */
>>
>> - ? ? ? if (f->page_size != 2048 && f->page_size != 512)
>> + ? ? ? if (f->page_size != 2048 && f->page_size != 512) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Current only support 2048 and 512
>> size\n");
>> ? ? ? ? ? ? ? ?return -EINVAL;
>> + ? ? ? }
>>
>> - ? ? ? if (f->flash_width != 16 && f->flash_width != 8)
>> + ? ? ? if (f->flash_width != 16 && f->flash_width != 8) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
>
> ? So this patch not only converts printk() to dev_*() calls, it also adds
> some dev_err() calls? This should have been mentioned in the change long at
> least...

What do you mean by "chang long"? Do you mean the cover letter or the
patch name?
Certainly it is a good suggest to show it in the change version list.

>
>> ? ? ? ? ? ? ? ?return -EINVAL;
>> + ? ? ? }
>>
>> ? ? ? ?/* calculate flash information */
>> ? ? ? ?info->cmdset =&default_cmdset;
>> @@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>> ? ? ? ?if (!ret) {
>> ? ? ? ? ? ? ? ?kfree(mtd);
>> ? ? ? ? ? ? ? ?info->mtd = NULL;
>> - ? ? ? ? ? ? ? printk(KERN_INFO "There is no nand chip on cs 0!\n");
>> + ? ? ? ? ? ? ? dev_info(&info->pdev->dev, "There is no nand chip on cs
>> 0!\n");
>>
>> ? ? ? ? ? ? ? ?return -EINVAL;
>> ? ? ? ?}
>> @@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
>> ? ? ? ?chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
>> ? ? ? ?id = *((uint16_t *)(info->data_buff));
>> ? ? ? ?if (id != 0)
>> - ? ? ? ? ? ? ? printk(KERN_INFO "Detect a flash id %x\n", id);
>> + ? ? ? ? ? ? ? dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
>> ? ? ? ?else {
>> ? ? ? ? ? ? ? ?kfree(mtd);
>> ? ? ? ? ? ? ? ?info->mtd = NULL;
>> - ? ? ? ? ? ? ? printk(KERN_WARNING "Read out ID 0, potential timing set
>> wrong!!\n");
>> + ? ? ? ? ? ? ? dev_warn(&info->pdev->dev, "Read out ID 0, "
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "potential timing set wrong!!\n");
>
> ? Don't break the string -- this prevents grepping for it.

Thanks, I would fix it.

Best regards,
Lei

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

* Re: [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-07-13 12:41                         ` Lei Wen
@ 2011-07-13 14:35                           ` Sergei Shtylyov
  -1 siblings, 0 replies; 162+ messages in thread
From: Sergei Shtylyov @ 2011-07-13 14:35 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Sergei Shtylyov, Artem Bityutskiy,
	Lei Wen, Yu Tang, Haojian Zhuang, Daniel Mack, linux-mtd,
	Igor Grinberg, linux-arm-kernel

Hello.

Lei Wen wrote:

>>> Signed-off-by: Lei Wen<leiwen@marvell.com>
>>> ---
>>>  drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
>>>  1 files changed, 16 insertions(+), 11 deletions(-)
>>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c
>>> b/drivers/mtd/nand/pxa3xx_nand.c
>>> index 1efecf7..afe14bd 100644
>>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>>> +++ b/drivers/mtd/nand/pxa3xx_nand.c
>> [...]
>>> @@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct
>>> pxa3xx_nand_info *info,
>>>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>>        uint32_t ndcr = 0x0; /* enable all interrupts */
>>>
>>> -       if (f->page_size != 2048 && f->page_size != 512)
>>> +       if (f->page_size != 2048 && f->page_size != 512) {
>>> +               dev_err(&pdev->dev, "Current only support 2048 and 512
>>> size\n");
>>>                return -EINVAL;
>>> +       }
>>>
>>> -       if (f->flash_width != 16 && f->flash_width != 8)
>>> +       if (f->flash_width != 16 && f->flash_width != 8) {
>>> +               dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");

>>   So this patch not only converts printk() to dev_*() calls, it also adds
>> some dev_err() calls? This should have been mentioned in the change long at
>> least...

> What do you mean by "chang long"? Do you mean the cover letter or the
> patch name?

    I mean the patch description (which in this patch is missing).

> Certainly it is a good suggest to show it in the change version list.

    Don't quite understand what you mean here...

> Best regards,
> Lei

WBR, Sergei

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

* [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-07-13 14:35                           ` Sergei Shtylyov
  0 siblings, 0 replies; 162+ messages in thread
From: Sergei Shtylyov @ 2011-07-13 14:35 UTC (permalink / raw)
  To: linux-arm-kernel

Hello.

Lei Wen wrote:

>>> Signed-off-by: Lei Wen<leiwen@marvell.com>
>>> ---
>>>  drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
>>>  1 files changed, 16 insertions(+), 11 deletions(-)
>>> diff --git a/drivers/mtd/nand/pxa3xx_nand.c
>>> b/drivers/mtd/nand/pxa3xx_nand.c
>>> index 1efecf7..afe14bd 100644
>>> --- a/drivers/mtd/nand/pxa3xx_nand.c
>>> +++ b/drivers/mtd/nand/pxa3xx_nand.c
>> [...]
>>> @@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct
>>> pxa3xx_nand_info *info,
>>>        struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
>>>        uint32_t ndcr = 0x0; /* enable all interrupts */
>>>
>>> -       if (f->page_size != 2048 && f->page_size != 512)
>>> +       if (f->page_size != 2048 && f->page_size != 512) {
>>> +               dev_err(&pdev->dev, "Current only support 2048 and 512
>>> size\n");
>>>                return -EINVAL;
>>> +       }
>>>
>>> -       if (f->flash_width != 16 && f->flash_width != 8)
>>> +       if (f->flash_width != 16 && f->flash_width != 8) {
>>> +               dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");

>>   So this patch not only converts printk() to dev_*() calls, it also adds
>> some dev_err() calls? This should have been mentioned in the change long at
>> least...

> What do you mean by "chang long"? Do you mean the cover letter or the
> patch name?

    I mean the patch description (which in this patch is missing).

> Certainly it is a good suggest to show it in the change version list.

    Don't quite understand what you mean here...

> Best regards,
> Lei

WBR, Sergei

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

* [PATCH V8 0/4] pxa3xx_nand: add two chip select support
  2011-07-12 14:35                     ` Lei Wen
@ 2011-07-15  3:44                       ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

V5:
Merge platform change patch and two chip feature into one patch.
Clean some format issue.

V6:
Add suspend and resume enhancement as a part of this patch set.

V7:
Fix build bug
Fix zylonite machine cannot resume back

V8:
Fix typos and code style

Lei Wen (4):
  MTD: pxa3xx_nand: enhance suspend and resume routine
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  453 ++++++++++++++++----------
 9 files changed, 327 insertions(+), 185 deletions(-)

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

* [PATCH V8 0/4] pxa3xx_nand: add two chip select support
@ 2011-07-15  3:44                       ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

V2:
Choose to at most keep original pxa3xx_nand_info structure to reduce
patch size

V3:
Seperate original one patch into a patch set

V4:
Further seperate patch into small pieces:
Mainly change make in two patch, which seperate do
the new structure definition and two chip select support add.

V5:
Merge platform change patch and two chip feature into one patch.
Clean some format issue.

V6:
Add suspend and resume enhancement as a part of this patch set.

V7:
Fix build bug
Fix zylonite machine cannot resume back

V8:
Fix typos and code style

Lei Wen (4):
  MTD: pxa3xx_nand: enhance suspend and resume routine
  MTD: pxa3xx_nand: convert all printk into dev_*
  MTD: pxa3xx_nand: sperate each chip individual info
  MTD: pxa3xx_nand: enable multiple chip select support

 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +-
 drivers/mtd/nand/pxa3xx_nand.c               |  453 ++++++++++++++++----------
 9 files changed, 327 insertions(+), 185 deletions(-)

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

* [PATCH V8 1/4] MTD: pxa3xx_nand: enhance suspend and resume routine
  2011-07-12 14:35                     ` Lei Wen
@ 2011-07-15  3:44                       ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

This patch add protection on the suspend&resume path to prevent
some unexpected behavior, like interrupt occur at the very second
of resume back and it don't follow normal command path, which lead
to bug.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   15 ++++++++++++++-
 1 files changed, 14 insertions(+), 1 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..61e1ff5 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1158,23 +1158,36 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct mtd_info *mtd = info->mtd;
 
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
+	mtd->suspend(mtd);
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct mtd_info *mtd = info->mtd;
+
+	/* We don't want to handle interrupt without calling mtd routine */
+	disable_int(info, NDCR_INT_MASK);
 
 	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
 	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
-	clk_enable(info->clk);
 
+	/*
+	 * As the spec says, the NDSR would be updated to 0x1800 when
+	 * doing the nand_clk disable/enable.
+	 * To prevent it damaging state machine of the driver, clear
+	 * all status before resume
+	 */
+	nand_writel(info, NDSR, NDSR_MASK);
+	mtd->resume(mtd);
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* [PATCH V8 1/4] MTD: pxa3xx_nand: enhance suspend and resume routine
@ 2011-07-15  3:44                       ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

This patch add protection on the suspend&resume path to prevent
some unexpected behavior, like interrupt occur at the very second
of resume back and it don't follow normal command path, which lead
to bug.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   15 ++++++++++++++-
 1 files changed, 14 insertions(+), 1 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b7db1b2..61e1ff5 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1158,23 +1158,36 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct mtd_info *mtd = info->mtd;
 
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
+	mtd->suspend(mtd);
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct mtd_info *mtd = info->mtd;
+
+	/* We don't want to handle interrupt without calling mtd routine */
+	disable_int(info, NDCR_INT_MASK);
 
 	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
 	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
-	clk_enable(info->clk);
 
+	/*
+	 * As the spec says, the NDSR would be updated to 0x1800 when
+	 * doing the nand_clk disable/enable.
+	 * To prevent it damaging state machine of the driver, clear
+	 * all status before resume
+	 */
+	nand_writel(info, NDSR, NDSR_MASK);
+	mtd->resume(mtd);
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* [PATCH V8 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
  2011-07-12 14:35                     ` Lei Wen
@ 2011-07-15  3:44                       ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

Also add missed warning message.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 61e1ff5..e55d7f8 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev,
+			 "Read out ID 0, potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH V8 2/4] MTD: pxa3xx_nand: convert all printk into dev_*
@ 2011-07-15  3:44                       ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

Also add missed warning message.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 61e1ff5..e55d7f8 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -359,7 +359,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 					DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -385,7 +385,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
 		break;
 	default:
-		printk(KERN_ERR "%s: invalid state %d\n", __func__,
+		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
 				info->state);
 		BUG();
 	}
@@ -616,8 +616,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	default:
 		exec_cmd = 0;
-		printk(KERN_ERR "pxa3xx-nand: non-supported"
-			" command %x\n", command);
+		dev_err(&info->pdev->dev, "non-supported command %x\n",
+				command);
 		break;
 	}
 
@@ -646,7 +646,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 		ret = wait_for_completion_timeout(&info->cmd_complete,
 				CHIP_DELAY_TIMEOUT);
 		if (!ret) {
-			printk(KERN_ERR "Wait time out!!!\n");
+			dev_err(&info->pdev->dev, "Wait time out!!!\n");
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
@@ -774,11 +774,15 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
-	if (f->page_size != 2048 && f->page_size != 512)
+	if (f->page_size != 2048 && f->page_size != 512) {
+		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
 		return -EINVAL;
+	}
 
-	if (f->flash_width != 16 && f->flash_width != 8)
+	if (f->flash_width != 16 && f->flash_width != 8) {
+		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
 		return -EINVAL;
+	}
 
 	/* calculate flash information */
 	info->cmdset = &default_cmdset;
@@ -898,7 +902,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (!ret) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_INFO "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
 		return -EINVAL;
 	}
@@ -906,11 +910,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
 	id = *((uint16_t *)(info->data_buff));
 	if (id != 0)
-		printk(KERN_INFO "Detect a flash id %x\n", id);
+		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+		dev_warn(&info->pdev->dev,
+			 "Read out ID 0, potential timing set wrong!!\n");
 
 		return -EINVAL;
 	}
@@ -930,7 +935,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
 		kfree(mtd);
 		info->mtd = NULL;
-		printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
-- 
1.7.0.4

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

* [PATCH V8 3/4] MTD: pxa3xx_nand: sperate each chip individual info
  2011-07-12 14:35                     ` Lei Wen
@ 2011-07-15  3:44                       ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  292 ++++++++++++++++++++++------------------
 1 files changed, 160 insertions(+), 132 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index e55d7f8..97b6894 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
 
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
+
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,52 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +867,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +874,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +884,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +916,10 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +927,6 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_warn(&info->pdev->dev,
 			 "Read out ID 0, potential timing set wrong!!\n");
 
@@ -933,14 +946,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -952,47 +968,56 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	pxa3xx_flash_ids[1].name = NULL;
 	def = pxa3xx_flash_ids;
 KEEP_CONFIG:
+	chip->ecc.mode = NAND_ECC_HW;
+	chip->ecc.size = host->page_size;
+
+	chip->options = NAND_NO_AUTOINCR;
+	chip->options |= NAND_NO_READRDY;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
+
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
-	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
-
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
-	chip->options |= NAND_NO_AUTOINCR;
-	chip->options |= NAND_NO_READRDY;
+		host->row_addr_cycles = 2;
 
+	mtd->name = mtd_names[0];
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1025,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1103,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1121,19 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
+
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1141,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1153,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1162,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1170,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1182,8 +1210,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	/* We don't want to handle interrupt without calling mtd routine */
 	disable_int(info, NDCR_INT_MASK);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 
 	/*
 	 * As the spec says, the NDSR would be updated to 0x1800 when
-- 
1.7.0.4

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

* [PATCH V8 3/4] MTD: pxa3xx_nand: sperate each chip individual info
@ 2011-07-15  3:44                       ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

For support two chip select, we seperate chip specific info in this
patch.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  292 ++++++++++++++++++++++------------------
 1 files changed, 160 insertions(+), 132 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index e55d7f8..97b6894 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -110,6 +110,7 @@ enum {
 
 enum {
 	STATE_IDLE = 0,
+	STATE_PREPARED,
 	STATE_CMD_HANDLE,
 	STATE_DMA_READING,
 	STATE_DMA_WRITING,
@@ -120,21 +121,39 @@ enum {
 	STATE_READY,
 };
 
-struct pxa3xx_nand_info {
-	struct nand_chip	nand_chip;
+struct pxa3xx_nand_host {
+	struct nand_chip	chip;
+	struct pxa3xx_nand_cmdset *cmdset;
+	struct mtd_info         *mtd;
+	void			*info_data;
+
+	/* page size of attached chip */
+	unsigned int		page_size;
+	int			use_ecc;
 
+	/* calculated from pxa3xx_nand_flash data */
+	unsigned int		col_addr_cycles;
+	unsigned int		row_addr_cycles;
+	size_t			read_id_bytes;
+
+	/* cached register value */
+	uint32_t		reg_ndcr;
+	uint32_t		ndtr0cs0;
+	uint32_t		ndtr1cs0;
+};
+
+struct pxa3xx_nand_info {
 	struct nand_hw_control	controller;
 	struct platform_device	 *pdev;
-	struct pxa3xx_nand_cmdset *cmdset;
 
 	struct clk		*clk;
 	void __iomem		*mmio_base;
 	unsigned long		mmio_phys;
+	struct completion	cmd_complete;
 
 	unsigned int 		buf_start;
 	unsigned int		buf_count;
 
-	struct mtd_info         *mtd;
 	/* DMA information */
 	int			drcmr_dat;
 	int			drcmr_cmd;
@@ -142,18 +161,11 @@ struct pxa3xx_nand_info {
 	unsigned char		*data_buff;
 	unsigned char		*oob_buff;
 	dma_addr_t 		data_buff_phys;
-	size_t			data_buff_size;
 	int 			data_dma_ch;
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	uint32_t		reg_ndcr;
-
-	/* saved column/page_addr during CMD_SEQIN */
-	int			seqin_column;
-	int			seqin_page_addr;
-
-	/* relate to the command */
+	struct pxa3xx_nand_host *host;
 	unsigned int		state;
 
 	int			use_ecc;	/* use HW ECC ? */
@@ -162,24 +174,13 @@ struct pxa3xx_nand_info {
 
 	unsigned int		page_size;	/* page size of attached chip */
 	unsigned int		data_size;	/* data size in FIFO */
+	unsigned int		oob_size;
 	int 			retcode;
-	struct completion 	cmd_complete;
 
 	/* generated NDCBx register values */
 	uint32_t		ndcb0;
 	uint32_t		ndcb1;
 	uint32_t		ndcb2;
-
-	/* timing calcuted from setting */
-	uint32_t		ndtr0cs0;
-	uint32_t		ndtr1cs0;
-
-	/* calculated from pxa3xx_nand_flash data */
-	size_t		oob_size;
-	size_t		read_id_bytes;
-
-	unsigned int	col_addr_cycles;
-	unsigned int	row_addr_cycles;
 };
 
 static int use_dma = 1;
@@ -241,9 +242,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000)
 
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 				   const struct pxa3xx_nand_timing *t)
 {
+	struct pxa3xx_nand_info *info = host->info_data;
 	unsigned long nand_clk = clk_get_rate(info->clk);
 	uint32_t ndtr0, ndtr1;
 
@@ -258,23 +260,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
 		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
 		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
-	info->ndtr0cs0 = ndtr0;
-	info->ndtr1cs0 = ndtr1;
+	host->ndtr0cs0 = ndtr0;
+	host->ndtr1cs0 = ndtr1;
 	nand_writel(info, NDTR0CS0, ndtr0);
 	nand_writel(info, NDTR1CS0, ndtr1);
 }
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
+	struct pxa3xx_nand_host *host = info->host;
+	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
-	info->data_size = info->page_size;
+	info->data_size = host->page_size;
 	if (!oob_enable) {
 		info->oob_size = 0;
 		return;
 	}
 
-	switch (info->page_size) {
+	switch (host->page_size) {
 	case 2048:
 		info->oob_size = (info->use_ecc) ? 40 : 64;
 		break;
@@ -292,9 +295,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr;
 
-	ndcr = info->reg_ndcr;
+	ndcr = host->reg_ndcr;
 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
 	ndcr |= NDCR_ND_RUN;
@@ -463,12 +467,6 @@ NORMAL_IRQ_EXIT:
 	return IRQ_HANDLED;
 }
 
-static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
-{
-	struct pxa3xx_nand_info *info = mtd->priv;
-	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
-}
-
 static inline int is_buf_blank(uint8_t *buf, size_t len)
 {
 	for (; len > 0; len--)
@@ -481,10 +479,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		uint16_t column, int page_addr)
 {
 	uint16_t cmd;
-	int addr_cycle, exec_cmd, ndcb0;
-	struct mtd_info *mtd = info->mtd;
+	int addr_cycle, exec_cmd;
+	struct pxa3xx_nand_host *host = info->host;
+	struct mtd_info *mtd = host->mtd;
 
-	ndcb0 = 0;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -494,6 +492,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
+	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
 
 	switch (command) {
@@ -512,20 +511,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 	}
 
-	info->ndcb0 = ndcb0;
-	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
-				    + info->col_addr_cycles);
+	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+				    + host->col_addr_cycles);
 
 	switch (command) {
 	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd = info->cmdset->read1;
+		cmd = host->cmdset->read1;
 		if (command == NAND_CMD_READOOB)
 			info->buf_start = mtd->writesize + column;
 		else
 			info->buf_start = column;
 
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))
 			info->ndcb0 |= NDCB0_CMD_TYPE(0)
 					| addr_cycle
 					| (cmd & NDCB0_CMD1_MASK);
@@ -537,7 +535,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 	case NAND_CMD_SEQIN:
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {
 			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
 
@@ -564,7 +562,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 			break;
 		}
 
-		cmd = info->cmdset->program;
+		cmd = host->cmdset->program;
 		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
 				| NDCB0_AUTO_RS
 				| NDCB0_ST_ROW_EN
@@ -574,8 +572,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_READID:
-		cmd = info->cmdset->read_id;
-		info->buf_count = info->read_id_bytes;
+		cmd = host->cmdset->read_id;
+		info->buf_count = host->read_id_bytes;
 		info->ndcb0 |= NDCB0_CMD_TYPE(3)
 				| NDCB0_ADDR_CYC(1)
 				| cmd;
@@ -583,7 +581,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		info->data_size = 8;
 		break;
 	case NAND_CMD_STATUS:
-		cmd = info->cmdset->read_status;
+		cmd = host->cmdset->read_status;
 		info->buf_count = 1;
 		info->ndcb0 |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -593,7 +591,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 		break;
 
 	case NAND_CMD_ERASE1:
-		cmd = info->cmdset->erase;
+		cmd = host->cmdset->erase;
 		info->ndcb0 |= NDCB0_CMD_TYPE(2)
 				| NDCB0_AUTO_RS
 				| NDCB0_ADDR_CYC(3)
@@ -604,7 +602,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 
 		break;
 	case NAND_CMD_RESET:
-		cmd = info->cmdset->reset;
+		cmd = host->cmdset->reset;
 		info->ndcb0 |= NDCB0_CMD_TYPE(5)
 				| cmd;
 
@@ -627,7 +625,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 				int column, int page_addr)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int ret, exec_cmd;
 
 	/*
@@ -635,9 +634,10 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	 * "byte" address into a "word" address appropriate
 	 * for indexing a word-oriented device
 	 */
-	if (info->reg_ndcr & NDCR_DWIDTH_M)
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
 		init_completion(&info->cmd_complete);
@@ -650,8 +650,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 			/* Stop State Machine for next command cycle */
 			pxa3xx_nand_stop(info);
 		}
-		info->state = STATE_IDLE;
 	}
+	info->state = STATE_IDLE;
 }
 
 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
@@ -664,7 +664,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, uint8_t *buf, int page)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -695,7 +696,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
 
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	char retval = 0xFF;
 
 	if (info->buf_start < info->buf_count)
@@ -707,7 +709,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 
 static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	u16 retval = 0xFFFF;
 
 	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
@@ -719,7 +722,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
 
 static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(buf, info->data_buff + info->buf_start, real_len);
@@ -729,7 +733,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
 		const uint8_t *buf, int len)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
 
 	memcpy(info->data_buff + info->buf_start, buf, real_len);
@@ -749,7 +754,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
 
 static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 
 	/* pxa3xx_nand_send_command has waited for command complete */
 	if (this->state == FL_WRITING || this->state == FL_ERASING) {
@@ -772,6 +778,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -785,45 +792,52 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 	}
 
 	/* calculate flash information */
-	info->cmdset = &default_cmdset;
-	info->page_size = f->page_size;
-	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+	host->cmdset = &default_cmdset;
+	host->page_size = f->page_size;
+	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
 	/* calculate addressing information */
-	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
 
 	if (f->num_blocks * f->page_per_block > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
+		host->row_addr_cycles = 2;
 
 	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
-	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+	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(info->read_id_bytes);
+	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
 	ndcr |= NDCR_SPARE_EN; /* enable spare by default */
 
-	info->reg_ndcr = ndcr;
+	host->reg_ndcr = ndcr;
 
-	pxa3xx_nand_set_timing(info, f->timing);
+	pxa3xx_nand_set_timing(host, f->timing);
 	return 0;
 }
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
+	struct pxa3xx_nand_host *host = info->host;
 	uint32_t ndcr = nand_readl(info, NDCR);
-	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-	/* set info fields needed to read id */
-	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
-	info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
-	info->cmdset = &default_cmdset;
 
-	info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
-	info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+	if (ndcr & NDCR_PAGE_SZ) {
+		host->page_size = 2048;
+		host->read_id_bytes = 4;
+	} else {
+		host->page_size = 512;
+		host->read_id_bytes = 2;
+	}
+
+	host->reg_ndcr = ndcr & ~NDCR_INT_MASK;
+	host->cmdset = &default_cmdset;
+
+	host->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);
 
 	return 0;
 }
@@ -853,7 +867,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 		return -ENOMEM;
 	}
 
-	info->data_buff_size = MAX_BUFF_SIZE;
 	info->data_desc = (void *)info->data_buff + data_desc_offset;
 	info->data_desc_addr = info->data_buff_phys + data_desc_offset;
 
@@ -861,7 +874,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 				pxa3xx_nand_data_dma_irq, info);
 	if (info->data_dma_ch < 0) {
 		dev_err(&pdev->dev, "failed to request data dma\n");
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 		return info->data_dma_ch;
 	}
@@ -871,21 +884,25 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->mtd;
-	struct nand_chip *chip = mtd->priv;
+	struct mtd_info *mtd = info->host->mtd;
+	int ret;
 
 	/* use the common timing to make a try */
-	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
-	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+	if (ret)
+		return ret;
+
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
 	if (info->is_ready)
-		return 1;
-	else
 		return 0;
+
+	return -ENODEV;
 }
 
 static int pxa3xx_nand_scan(struct mtd_info *mtd)
 {
-	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand_host *host = mtd->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
 	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
@@ -899,12 +916,10 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 		goto KEEP_CONFIG;
 
 	ret = pxa3xx_nand_sensing(info);
-	if (!ret) {
-		kfree(mtd);
-		info->mtd = NULL;
+	if (ret) {
 		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
 
-		return -EINVAL;
+		return ret;
 	}
 
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
@@ -912,8 +927,6 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	if (id != 0)
 		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
 	else {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_warn(&info->pdev->dev,
 			 "Read out ID 0, potential timing set wrong!!\n");
 
@@ -933,14 +946,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	}
 
 	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
-		kfree(mtd);
-		info->mtd = NULL;
 		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
 
 		return -EINVAL;
 	}
 
-	pxa3xx_nand_config_flash(info, f);
+	ret = pxa3xx_nand_config_flash(info, f);
+	if (ret) {
+		dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
+		return ret;
+	}
+
 	pxa3xx_flash_ids[0].name = f->name;
 	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
 	pxa3xx_flash_ids[0].pagesize = f->page_size;
@@ -952,47 +968,56 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 	pxa3xx_flash_ids[1].name = NULL;
 	def = pxa3xx_flash_ids;
 KEEP_CONFIG:
+	chip->ecc.mode = NAND_ECC_HW;
+	chip->ecc.size = host->page_size;
+
+	chip->options = NAND_NO_AUTOINCR;
+	chip->options |= NAND_NO_READRDY;
+	if (host->reg_ndcr & NDCR_DWIDTH_M)
+		chip->options |= NAND_BUSWIDTH_16;
+
 	if (nand_scan_ident(mtd, 1, def))
 		return -ENODEV;
 	/* calculate addressing information */
-	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+	if (mtd->writesize >= 2048)
+		host->col_addr_cycles = 2;
+	else
+		host->col_addr_cycles = 1;
+
 	info->oob_buff = info->data_buff + mtd->writesize;
 	if ((mtd->size >> chip->page_shift) > 65536)
-		info->row_addr_cycles = 3;
+		host->row_addr_cycles = 3;
 	else
-		info->row_addr_cycles = 2;
-	mtd->name = mtd_names[0];
-	chip->ecc.mode = NAND_ECC_HW;
-	chip->ecc.size = info->page_size;
-
-	chip->options = (info->reg_ndcr & NDCR_DWIDTH_M) ? NAND_BUSWIDTH_16 : 0;
-	chip->options |= NAND_NO_AUTOINCR;
-	chip->options |= NAND_NO_READRDY;
+		host->row_addr_cycles = 2;
 
+	mtd->name = mtd_names[0];
 	return nand_scan_tail(mtd);
 }
 
-static
-struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
+static int alloc_nand_resource(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info;
+	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
 	int ret, irq;
 
-	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
 			GFP_KERNEL);
-	if (!mtd) {
+	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
-	info = (struct pxa3xx_nand_info *)(&mtd[1]);
+	mtd = (struct mtd_info *)(&info[1]);
 	chip = (struct nand_chip *)(&mtd[1]);
+	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->mtd = mtd;
-	mtd->priv = info;
+	info->host = host;
+	host->mtd = mtd;
+	host->info_data = info;
+	mtd->priv = host;
 	mtd->owner = THIS_MODULE;
 
 	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
@@ -1000,7 +1025,6 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 	chip->controller        = &info->controller;
 	chip->waitfunc		= pxa3xx_nand_waitfunc;
 	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->dev_ready		= pxa3xx_nand_dev_ready;
 	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
 	chip->read_word		= pxa3xx_nand_read_word;
 	chip->read_byte		= pxa3xx_nand_read_byte;
@@ -1079,13 +1103,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, info);
 
-	return info;
+	return 0;
 
 fail_free_buf:
 	free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_coherent(&pdev->dev, info->data_buff_size,
+		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,
 			info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1097,17 +1121,19 @@ fail_put_clk:
 	clk_disable(info->clk);
 	clk_put(info->clk);
 fail_free_mtd:
-	kfree(mtd);
-	return NULL;
+	kfree(info);
+	return ret;
 }
 
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
 	struct resource *r;
 	int irq;
 
+	if (!info)
+		return 0;
+
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1115,7 +1141,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 		free_irq(irq, info);
 	if (use_dma) {
 		pxa_free_dma(info->data_dma_ch);
-		dma_free_writecombine(&pdev->dev, info->data_buff_size,
+		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,
 				info->data_buff, info->data_buff_phys);
 	} else
 		kfree(info->data_buff);
@@ -1127,10 +1153,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	if (mtd) {
-		nand_release(mtd);
-		kfree(mtd);
-	}
+	nand_release(info->host->mtd);
+	kfree(info);
 	return 0;
 }
 
@@ -1138,6 +1162,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
+	int ret;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1145,17 +1170,20 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	info = alloc_nand_resource(pdev);
-	if (info == NULL)
-		return -ENOMEM;
+	ret = alloc_nand_resource(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "alloc nand resource failed\n");
+		return ret;
+	}
 
-	if (pxa3xx_nand_scan(info->mtd)) {
+	info = platform_get_drvdata(pdev);
+	if (pxa3xx_nand_scan(info->host->mtd)) {
 		dev_err(&pdev->dev, "failed to scan nand\n");
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->mtd, NULL, 0,
+	return mtd_device_parse_register(info->host->mtd, NULL, 0,
 			pdata->parts, pdata->nr_parts);
 }
 
@@ -1182,8 +1210,8 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	/* We don't want to handle interrupt without calling mtd routine */
 	disable_int(info, NDCR_INT_MASK);
 
-	nand_writel(info, NDTR0CS0, info->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
+	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
 
 	/*
 	 * As the spec says, the NDSR would be updated to 0x1800 when
-- 
1.7.0.4

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

* [PATCH V8 4/4] MTD: pxa3xx_nand: enable multiple chip select support
  2011-07-12 14:35                     ` Lei Wen
@ 2011-07-15  3:44                       ` Lei Wen
  -1 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: Daniel Mack, Artem Bityutskiy, Igor Grinberg, Eric Miao,
	David Woodhouse, Haojian Zhuang, linux-mtd, linux-arm-kernel,
	Yu Tang

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  173 ++++++++++++++++++--------
 9 files changed, 164 insertions(+), 68 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 97b6894..9eb7f87 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -884,9 +915,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -917,7 +948,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
+			 info->cs);
 
 		return ret;
 	}
@@ -996,41 +1028,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1128,12 +1166,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1153,7 +1193,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1162,7 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1177,41 +1218,69 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand_platform_data *pdata;
+	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
-	mtd->suspend(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = info->host[cs]->mtd;
+		mtd->suspend(mtd);
+	}
+
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand_platform_data *pdata;
+	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	/* We don't want to handle interrupt without calling mtd routine */
 	disable_int(info, NDCR_INT_MASK);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 
 	/*
 	 * As the spec says, the NDSR would be updated to 0x1800 when
@@ -1220,7 +1289,11 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	 * all status before resume
 	 */
 	nand_writel(info, NDSR, NDSR_MASK);
-	mtd->resume(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = info->host[cs]->mtd;
+		mtd->resume(mtd);
+	}
+
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* [PATCH V8 4/4] MTD: pxa3xx_nand: enable multiple chip select support
@ 2011-07-15  3:44                       ` Lei Wen
  0 siblings, 0 replies; 162+ messages in thread
From: Lei Wen @ 2011-07-15  3:44 UTC (permalink / raw)
  To: linux-arm-kernel

Current pxa3xx_nand controller has two chip select which
both be workable. This patch enable this feature.

Update platform driver to support this feature.

Another 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.

Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 arch/arm/mach-mmp/aspenite.c                 |    5 +-
 arch/arm/mach-pxa/cm-x300.c                  |    5 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c           |    5 +-
 arch/arm/mach-pxa/littleton.c                |    5 +-
 arch/arm/mach-pxa/mxm8x10.c                  |    9 +-
 arch/arm/mach-pxa/raumfeld.c                 |    5 +-
 arch/arm/mach-pxa/zylonite.c                 |    5 +-
 arch/arm/plat-pxa/include/plat/pxa3xx_nand.h |   20 +++-
 drivers/mtd/nand/pxa3xx_nand.c               |  173 ++++++++++++++++++--------
 9 files changed, 164 insertions(+), 68 deletions(-)

diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 06b5fa8..c4996f3 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -167,8 +167,9 @@ static struct mtd_partition aspenite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data aspenite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= aspenite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(aspenite_nand_partitions),
+	.num_cs = 1,
+	.parts[0]	= aspenite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(aspenite_nand_partitions),
 };
 
 static struct i2c_board_info aspenite_i2c_info[] __initdata = {
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index b2248e7..969d252 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -423,8 +423,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= cm_x300_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= cm_x300_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c
index 3f9be41..2b8ca0d 100644
--- a/arch/arm/mach-pxa/colibri-pxa3xx.c
+++ b/arch/arm/mach-pxa/colibri-pxa3xx.c
@@ -139,8 +139,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= colibri_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= colibri_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index e5e326d..f6d1f9d 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -325,8 +325,9 @@ static struct mtd_partition littleton_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= littleton_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= littleton_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c
index b5a8fd3..90928d6 100644
--- a/arch/arm/mach-pxa/mxm8x10.c
+++ b/arch/arm/mach-pxa/mxm8x10.c
@@ -389,10 +389,11 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter = 1,
-	.keep_config = 1,
-	.parts = mxm_8x10_nand_partitions,
-	.nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.enable_arbiter	= 1,
+	.keep_config	= 1,
+	.num_cs		= 1,
+	.parts[0]	= mxm_8x10_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index d130f77..c7ec847 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -349,8 +349,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
 	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.parts		= raumfeld_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= raumfeld_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 5821185..64fdac9 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -366,8 +366,9 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
 	.enable_arbiter	= 1,
-	.parts		= zylonite_nand_partitions,
-	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
+	.num_cs		= 1,
+	.parts[0]	= zylonite_nand_partitions,
+	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
index 442301f..c42f39f 100644
--- a/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
+++ b/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
@@ -41,6 +41,19 @@ struct pxa3xx_nand_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
@@ -52,8 +65,11 @@ struct pxa3xx_nand_platform_data {
 	/* allow platform code to keep OBM/bootloader defined NFC config */
 	int	keep_config;
 
-	const struct mtd_partition		*parts;
-	unsigned int				nr_parts;
+	/* indicate how many chip selects will be used */
+	int	num_cs;
+
+	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;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 97b6894..9eb7f87 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -130,6 +130,7 @@ struct pxa3xx_nand_host {
 	/* page size of attached chip */
 	unsigned int		page_size;
 	int			use_ecc;
+	int			cs;
 
 	/* calculated from pxa3xx_nand_flash data */
 	unsigned int		col_addr_cycles;
@@ -165,9 +166,10 @@ struct pxa3xx_nand_info {
 	struct pxa_dma_desc	*data_desc;
 	dma_addr_t 		data_desc_addr;
 
-	struct pxa3xx_nand_host *host;
+	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
 	unsigned int		state;
 
+	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
 	int			use_dma;	/* use DMA ? */
 	int			is_ready;
@@ -226,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
 /* Define a default flash type setting serve as flash detecting only */
 #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
-const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};
 
 #define NDTR0_tCH(c)	(min((c), 7) << 19)
 #define NDTR0_tCS(c)	(min((c), 7) << 16)
@@ -268,7 +270,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
 
 static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN;
 
 	info->data_size = host->page_size;
@@ -295,7 +297,7 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
  */
 static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr;
 
 	ndcr = host->reg_ndcr;
@@ -420,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 {
 	struct pxa3xx_nand_info *info = devid;
 	unsigned int status, is_completed = 0;
+	unsigned int ready, cmd_done;
+
+	if (info->cs == 0) {
+		ready           = NDSR_FLASH_RDY;
+		cmd_done        = NDSR_CS0_CMDD;
+	} else {
+		ready           = NDSR_RDY;
+		cmd_done        = NDSR_CS1_CMDD;
+	}
 
 	status = nand_readl(info, NDSR);
 
@@ -441,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 			handle_data_pio(info);
 		}
 	}
-	if (status & NDSR_CS0_CMDD) {
+	if (status & cmd_done) {
 		info->state = STATE_CMD_DONE;
 		is_completed = 1;
 	}
-	if (status & NDSR_FLASH_RDY) {
+	if (status & ready) {
 		info->is_ready = 1;
 		info->state = STATE_READY;
 	}
@@ -480,9 +491,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 {
 	uint16_t cmd;
 	int addr_cycle, exec_cmd;
-	struct pxa3xx_nand_host *host = info->host;
-	struct mtd_info *mtd = host->mtd;
+	struct pxa3xx_nand_host *host;
+	struct mtd_info *mtd;
 
+	host = info->host[info->cs];
+	mtd = host->mtd;
 	addr_cycle = 0;
 	exec_cmd = 1;
 
@@ -492,8 +505,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
 	info->oob_size		= 0;
 	info->use_ecc		= 0;
 	info->is_ready		= 0;
-	info->ndcb0		= 0;
 	info->retcode		= ERR_NONE;
+	if (info->cs != 0)
+		info->ndcb0 = NDCB0_CSEL;
+	else
+		info->ndcb0 = 0;
 
 	switch (command) {
 	case NAND_CMD_READ0:
@@ -637,6 +653,17 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 	if (host->reg_ndcr & NDCR_DWIDTH_M)
 		column /= 2;
 
+	/*
+	 * There may be different NAND chip hooked to
+	 * different chip select, so check whether
+	 * chip select has been changed, if yes, reset the timing
+	 */
+	if (info->cs != host->cs) {
+		info->cs = host->cs;
+		nand_writel(info, NDTR0CS0, host->ndtr0cs0);
+		nand_writel(info, NDTR1CS0, host->ndtr1cs0);
+	}
+
 	info->state = STATE_PREPARED;
 	exec_cmd = prepare_command_pool(info, command, column, page_addr);
 	if (exec_cmd) {
@@ -778,7 +805,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 {
 	struct platform_device *pdev = info->pdev;
 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
-	struct pxa3xx_nand_host *host = info->host;
+	struct pxa3xx_nand_host *host = info->host[info->cs];
 	uint32_t ndcr = 0x0; /* enable all interrupts */
 
 	if (f->page_size != 2048 && f->page_size != 512) {
@@ -822,7 +849,11 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
 
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
-	struct pxa3xx_nand_host *host = info->host;
+	/*
+	 * 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
+	 */
+	struct pxa3xx_nand_host *host = info->host[0];
 	uint32_t ndcr = nand_readl(info, NDCR);
 
 	if (ndcr & NDCR_PAGE_SZ) {
@@ -884,9 +915,9 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
 
 static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
 {
-	struct mtd_info *mtd = info->host->mtd;
+	struct mtd_info *mtd;
 	int ret;
-
+	mtd = info->host[info->cs]->mtd;
 	/* use the common timing to make a try */
 	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
 	if (ret)
@@ -917,7 +948,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 
 	ret = pxa3xx_nand_sensing(info);
 	if (ret) {
-		dev_info(&info->pdev->dev, "There is no nand chip on cs 0!\n");
+		dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
+			 info->cs);
 
 		return ret;
 	}
@@ -996,41 +1028,47 @@ KEEP_CONFIG:
 
 static int alloc_nand_resource(struct platform_device *pdev)
 {
+	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
 	struct pxa3xx_nand_host *host;
 	struct nand_chip *chip;
 	struct mtd_info *mtd;
 	struct resource *r;
-	int ret, irq;
+	int ret, irq, cs;
 
-	info = kzalloc(sizeof(*info) + sizeof(*mtd) + sizeof(*host),
-			GFP_KERNEL);
+	pdata = pdev->dev.platform_data;
+	info = kzalloc(sizeof(*info) + (sizeof(*mtd) +
+		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
 	if (!info) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	mtd = (struct mtd_info *)(&info[1]);
-	chip = (struct nand_chip *)(&mtd[1]);
-	host = (struct pxa3xx_nand_host *)chip;
 	info->pdev = pdev;
-	info->host = host;
-	host->mtd = mtd;
-	host->info_data = info;
-	mtd->priv = host;
-	mtd->owner = THIS_MODULE;
-
-	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
-	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
-	chip->controller        = &info->controller;
-	chip->waitfunc		= pxa3xx_nand_waitfunc;
-	chip->select_chip	= pxa3xx_nand_select_chip;
-	chip->cmdfunc		= pxa3xx_nand_cmdfunc;
-	chip->read_word		= pxa3xx_nand_read_word;
-	chip->read_byte		= pxa3xx_nand_read_byte;
-	chip->read_buf		= pxa3xx_nand_read_buf;
-	chip->write_buf		= pxa3xx_nand_write_buf;
-	chip->verify_buf	= pxa3xx_nand_verify_buf;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = (struct mtd_info *)((unsigned int)&info[1] +
+		      (sizeof(*mtd) + sizeof(*host)) * cs);
+		chip = (struct nand_chip *)(&mtd[1]);
+		host = (struct pxa3xx_nand_host *)chip;
+		info->host[cs] = host;
+		host->mtd = mtd;
+		host->cs = cs;
+		host->info_data = info;
+		mtd->priv = host;
+		mtd->owner = THIS_MODULE;
+
+		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->controller        = &info->controller;
+		chip->waitfunc		= pxa3xx_nand_waitfunc;
+		chip->select_chip	= pxa3xx_nand_select_chip;
+		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
+		chip->read_word		= pxa3xx_nand_read_word;
+		chip->read_byte		= pxa3xx_nand_read_byte;
+		chip->read_buf		= pxa3xx_nand_read_buf;
+		chip->write_buf		= pxa3xx_nand_write_buf;
+		chip->verify_buf	= pxa3xx_nand_verify_buf;
+	}
 
 	spin_lock_init(&chip->controller->lock);
 	init_waitqueue_head(&chip->controller->wq);
@@ -1128,12 +1166,14 @@ fail_free_mtd:
 static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+	struct pxa3xx_nand_platform_data *pdata;
 	struct resource *r;
-	int irq;
+	int irq, cs;
 
 	if (!info)
 		return 0;
 
+	pdata = pdev->dev.platform_data;
 	platform_set_drvdata(pdev, NULL);
 
 	irq = platform_get_irq(pdev, 0);
@@ -1153,7 +1193,8 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 	clk_disable(info->clk);
 	clk_put(info->clk);
 
-	nand_release(info->host->mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++)
+		nand_release(info->host[cs]->mtd);
 	kfree(info);
 	return 0;
 }
@@ -1162,7 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_platform_data *pdata;
 	struct pxa3xx_nand_info *info;
-	int ret;
+	int ret, cs, probe_success;
 
 	pdata = pdev->dev.platform_data;
 	if (!pdata) {
@@ -1177,41 +1218,69 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 	}
 
 	info = platform_get_drvdata(pdev);
-	if (pxa3xx_nand_scan(info->host->mtd)) {
-		dev_err(&pdev->dev, "failed to scan nand\n");
+	probe_success = 0;
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		info->cs = cs;
+		ret = pxa3xx_nand_scan(info->host[cs]->mtd);
+		if (ret) {
+			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
+				cs);
+			continue;
+		}
+
+		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
+				pdata->parts[cs], pdata->nr_parts[cs]);
+		if (!ret)
+			probe_success = 1;
+	}
+
+	if (!probe_success) {
 		pxa3xx_nand_remove(pdev);
 		return -ENODEV;
 	}
 
-	return mtd_device_parse_register(info->host->mtd, NULL, 0,
-			pdata->parts, pdata->nr_parts);
+	return 0;
 }
 
 #ifdef CONFIG_PM
 static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand_platform_data *pdata;
+	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	if (info->state) {
 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
 		return -EAGAIN;
 	}
 
-	mtd->suspend(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = info->host[cs]->mtd;
+		mtd->suspend(mtd);
+	}
+
 	return 0;
 }
 
 static int pxa3xx_nand_resume(struct platform_device *pdev)
 {
 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
-	struct mtd_info *mtd = info->mtd;
+	struct pxa3xx_nand_platform_data *pdata;
+	struct mtd_info *mtd;
+	int cs;
 
+	pdata = pdev->dev.platform_data;
 	/* We don't want to handle interrupt without calling mtd routine */
 	disable_int(info, NDCR_INT_MASK);
 
-	nand_writel(info, NDTR0CS0, info->host->ndtr0cs0);
-	nand_writel(info, NDTR1CS0, info->host->ndtr1cs0);
+	/*
+	 * Directly set the chip select to a invalid value,
+	 * then the driver would reset the timing according
+	 * to current chip select at the beginning of cmdfunc
+	 */
+	info->cs = 0xff;
 
 	/*
 	 * As the spec says, the NDSR would be updated to 0x1800 when
@@ -1220,7 +1289,11 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
 	 * all status before resume
 	 */
 	nand_writel(info, NDSR, NDSR_MASK);
-	mtd->resume(mtd);
+	for (cs = 0; cs < pdata->num_cs; cs++) {
+		mtd = info->host[cs]->mtd;
+		mtd->resume(mtd);
+	}
+
 	return 0;
 }
 #else
-- 
1.7.0.4

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

* Re: [PATCH V8 0/4] pxa3xx_nand: add two chip select support
  2011-07-15  3:44                       ` Lei Wen
@ 2011-07-20  4:51                         ` Artem Bityutskiy
  -1 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-07-20  4:51 UTC (permalink / raw)
  To: Lei Wen
  Cc: Eric Miao, David Woodhouse, Yu Tang, Haojian Zhuang, Daniel Mack,
	linux-mtd, Igor Grinberg, linux-arm-kernel

On Thu, 2011-07-14 at 20:44 -0700, Lei Wen wrote:
> V2:
> Choose to at most keep original pxa3xx_nand_info structure to reduce
> patch size
> 
> V3:
> Seperate original one patch into a patch set
> 
> V4:
> Further seperate patch into small pieces:
> Mainly change make in two patch, which seperate do
> the new structure definition and two chip select support add.
> 
> V5:
> Merge platform change patch and two chip feature into one patch.
> Clean some format issue.
> 
> V6:
> Add suspend and resume enhancement as a part of this patch set.
> 
> V7:
> Fix build bug
> Fix zylonite machine cannot resume back
> 
> V8:
> Fix typos and code style

Pushed the series to the l2-mtd-2.6.git, thanks.

-- 
Best Regards,
Artem Bityutskiy

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

* [PATCH V8 0/4] pxa3xx_nand: add two chip select support
@ 2011-07-20  4:51                         ` Artem Bityutskiy
  0 siblings, 0 replies; 162+ messages in thread
From: Artem Bityutskiy @ 2011-07-20  4:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 2011-07-14 at 20:44 -0700, Lei Wen wrote:
> V2:
> Choose to at most keep original pxa3xx_nand_info structure to reduce
> patch size
> 
> V3:
> Seperate original one patch into a patch set
> 
> V4:
> Further seperate patch into small pieces:
> Mainly change make in two patch, which seperate do
> the new structure definition and two chip select support add.
> 
> V5:
> Merge platform change patch and two chip feature into one patch.
> Clean some format issue.
> 
> V6:
> Add suspend and resume enhancement as a part of this patch set.
> 
> V7:
> Fix build bug
> Fix zylonite machine cannot resume back
> 
> V8:
> Fix typos and code style

Pushed the series to the l2-mtd-2.6.git, thanks.

-- 
Best Regards,
Artem Bityutskiy

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

end of thread, other threads:[~2011-07-20  4:51 UTC | newest]

Thread overview: 162+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-06-22  3:17 [PATCH] MTD: pxa3xx_nand: enable multiple chip select support Lei Wen
2011-07-08 16:00 ` Lei Wen
2011-06-22 11:39 ` Daniel Mack
2011-06-22 11:39   ` Daniel Mack
2011-06-22 12:21   ` Lei Wen
2011-07-08 17:38     ` Lei Wen
2011-06-22 13:06     ` Daniel Mack
2011-06-22 13:06       ` Daniel Mack
2011-06-23  6:22       ` Lei Wen
2011-06-23  6:22         ` Lei Wen
2011-06-22 13:45 ` Igor Grinberg
2011-07-08 16:09   ` Igor Grinberg
2011-06-23  6:35   ` Lei Wen
2011-07-08 15:26     ` Lei Wen
2011-06-23 10:44     ` Igor Grinberg
2011-06-23 10:44       ` Igor Grinberg
2011-06-25 11:17       ` Lei Wen
2011-06-25 11:17         ` Lei Wen
2011-06-25 12:32         ` Daniel Mack
2011-06-25 12:32           ` Daniel Mack
2011-06-25 12:51           ` Lei Wen
2011-06-25 12:51             ` Lei Wen
2011-06-27 13:22         ` Igor Grinberg
2011-06-27 13:22           ` Igor Grinberg
2011-06-28  7:32         ` Artem Bityutskiy
2011-06-28  7:32           ` Artem Bityutskiy
2011-06-28 15:12           ` Lei Wen
2011-06-28 15:12             ` Lei Wen
2011-06-29  3:51           ` [PATCH V3 0/9] pxa3xx_nand: add two " Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  9:00             ` Igor Grinberg
2011-06-29  9:00               ` Igor Grinberg
2011-06-29 10:20               ` Artem Bityutskiy
2011-06-29 10:20                 ` Artem Bityutskiy
2011-07-04  9:27                 ` Lei Wen
2011-07-04  9:27                   ` Lei Wen
2011-07-04  9:25             ` [PATCH V4 0/4] " Lei Wen
2011-07-04  9:25               ` Lei Wen
2011-07-08  3:34               ` [PATCH V5 0/3] " Lei Wen
2011-07-08  3:34                 ` Lei Wen
2011-07-12 10:28                 ` [PATCH V6 0/4] " Lei Wen
2011-07-12 10:28                   ` Lei Wen
2011-07-12 14:35                   ` [PATCH V7 " Lei Wen
2011-07-12 14:35                     ` Lei Wen
2011-07-15  3:44                     ` [PATCH V8 " Lei Wen
2011-07-15  3:44                       ` Lei Wen
2011-07-20  4:51                       ` Artem Bityutskiy
2011-07-20  4:51                         ` Artem Bityutskiy
2011-07-15  3:44                     ` [PATCH V8 1/4] MTD: pxa3xx_nand: enhance suspend and resume routine Lei Wen
2011-07-15  3:44                       ` Lei Wen
2011-07-15  3:44                     ` [PATCH V8 2/4] MTD: pxa3xx_nand: convert all printk into dev_* Lei Wen
2011-07-15  3:44                       ` Lei Wen
2011-07-15  3:44                     ` [PATCH V8 3/4] MTD: pxa3xx_nand: sperate each chip individual info Lei Wen
2011-07-15  3:44                       ` Lei Wen
2011-07-15  3:44                     ` [PATCH V8 4/4] MTD: pxa3xx_nand: enable multiple chip select support Lei Wen
2011-07-15  3:44                       ` Lei Wen
2011-07-12 14:35                   ` [PATCH V7 1/4] MTD: pxa3xx_nand: enhance suspend and resume routine Lei Wen
2011-07-12 14:35                     ` Lei Wen
2011-07-13 10:53                     ` Sergei Shtylyov
2011-07-13 10:53                       ` Sergei Shtylyov
2011-07-12 14:35                   ` [PATCH V7 2/4] MTD: pxa3xx_nand: convert all printk into dev_* Lei Wen
2011-07-12 14:35                     ` Lei Wen
2011-07-13 10:57                     ` Sergei Shtylyov
2011-07-13 10:57                       ` Sergei Shtylyov
2011-07-13 12:41                       ` Lei Wen
2011-07-13 12:41                         ` Lei Wen
2011-07-13 14:35                         ` Sergei Shtylyov
2011-07-13 14:35                           ` Sergei Shtylyov
2011-07-12 14:35                   ` [PATCH V7 3/4] MTD: pxa3xx_nand: sperate each chip individual info Lei Wen
2011-07-12 14:35                     ` Lei Wen
2011-07-12 14:35                   ` [PATCH V7 4/4] MTD: pxa3xx_nand: enable multiple chip select support Lei Wen
2011-07-12 14:35                     ` Lei Wen
2011-07-12 10:28                 ` [PATCH V6 1/4] MTD: pxa3xx_nand: convert all printk into dev_* Lei Wen
2011-07-12 10:28                   ` Lei Wen
2011-07-12 10:28                 ` [PATCH V6 2/4] MTD: pxa3xx_nand: sperate each chip individual info Lei Wen
2011-07-12 10:28                   ` Lei Wen
2011-07-12 10:28                 ` [PATCH V6 3/4] MTD: pxa3xx_nand: enable multiple chip select support Lei Wen
2011-07-12 10:28                   ` Lei Wen
2011-07-12 10:28                 ` [PATCH V6 4/4] MTD: pxa3xx_nand: enhance suspend and resume routine Lei Wen
2011-07-12 10:28                   ` Lei Wen
2011-07-12 11:39                   ` Daniel Mack
2011-07-12 11:39                     ` Daniel Mack
2011-07-12 12:02                     ` Daniel Mack
2011-07-12 12:02                       ` Daniel Mack
2011-07-12 15:56                       ` Igor Grinberg
2011-07-12 15:56                         ` Igor Grinberg
2011-07-12 17:35                         ` Daniel Mack
2011-07-12 17:35                           ` Daniel Mack
2011-07-08  3:34               ` [PATCH 1/3] MTD: pxa3xx_nand: convert all printk into dev_* Lei Wen
2011-07-08  3:34                 ` Lei Wen
2011-07-08  3:34               ` [PATCH 2/3] MTD: pxa3xx_nand: sperate each chip individual info Lei Wen
2011-07-08  3:34                 ` Lei Wen
2011-07-08  3:34               ` [PATCH 3/3] MTD: pxa3xx_nand: enable multiple chip select support Lei Wen
2011-07-08  3:34                 ` Lei Wen
2011-07-04  9:25             ` [PATCH 1/4] MTD: pxa3xx_nand: convert all printk into dev_* Lei Wen
2011-07-04  9:25               ` Lei Wen
2011-07-04  9:25             ` [PATCH 2/4] MTD: pxa3xx_nand: sperate each chip individual info Lei Wen
2011-07-04  9:25               ` Lei Wen
2011-07-06  7:29               ` Igor Grinberg
2011-07-06  7:29                 ` Igor Grinberg
2011-07-04  9:25             ` [PATCH 3/4] MTD: pxa3xx_nand: enable multiple chip select support Lei Wen
2011-07-04  9:25               ` Lei Wen
2011-07-06  6:53               ` Artem Bityutskiy
2011-07-06  6:53                 ` Artem Bityutskiy
2011-07-06  6:54                 ` Lei Wen
2011-07-06  6:54                   ` Lei Wen
2011-07-06  7:07                   ` Artem Bityutskiy
2011-07-06  7:07                     ` Artem Bityutskiy
2011-07-06  7:41               ` Igor Grinberg
2011-07-06  7:41                 ` Igor Grinberg
2011-07-07  6:26                 ` Lei Wen
2011-07-07  6:26                   ` Lei Wen
2011-07-07  8:59                   ` Igor Grinberg
2011-07-07  8:59                     ` Igor Grinberg
2011-07-07  9:06                     ` Lei Wen
2011-07-07  9:06                       ` Lei Wen
2011-07-07 11:23                       ` Igor Grinberg
2011-07-07 11:23                         ` Igor Grinberg
2011-07-11 14:49                 ` Daniel Mack
2011-07-11 14:49                   ` Daniel Mack
2011-07-11 18:19                   ` Igor Grinberg
2011-07-11 18:19                     ` Igor Grinberg
2011-07-11 18:53                     ` Daniel Mack
2011-07-11 18:53                       ` Daniel Mack
2011-07-11 19:25                       ` Igor Grinberg
2011-07-11 19:25                         ` Igor Grinberg
2011-07-12  9:40                         ` Lei Wen
2011-07-12  9:40                           ` Lei Wen
2011-07-12  9:57                           ` Daniel Mack
2011-07-12  9:57                             ` Daniel Mack
2011-07-12 10:29                             ` Lei Wen
2011-07-12 10:29                               ` Lei Wen
2011-07-12 12:05                               ` Daniel Mack
2011-07-12 12:05                                 ` Daniel Mack
2011-07-12 12:48                         ` Daniel Mack
2011-07-12 12:48                           ` Daniel Mack
2011-07-12 15:49                           ` Igor Grinberg
2011-07-12 15:49                             ` Igor Grinberg
2011-07-04  9:25             ` [PATCH 4/4] ARM: mmp/pxa: fix nand platform data Lei Wen
2011-07-04  9:25               ` Lei Wen
2011-06-29  3:51           ` [PATCH 1/9] MTD: pxa3xx_nand: convert all printk into dev_* Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  3:51           ` [PATCH 2/9] MTD: pxa3xx_nand: enable multiple chip select support Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  7:11             ` Artem Bityutskiy
2011-06-29  7:11               ` Artem Bityutskiy
2011-06-29  7:16               ` Lei Wen
2011-06-29  7:16                 ` Lei Wen
2011-06-29  3:51           ` [PATCH 3/9] ARM: aspenite: fix nand platform data Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  3:51           ` [PATCH 4/9] ARM: cm-x300: " Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  3:51           ` [PATCH 5/9] ARM: colibri-pxa3xx: " Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  3:51           ` [PATCH 6/9] ARM: littleton: " Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  3:51           ` [PATCH 7/9] ARM: mxm8x10: " Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  3:51           ` [PATCH 8/9] ARM: raumfeld: " Lei Wen
2011-06-29  3:51             ` Lei Wen
2011-06-29  3:51           ` [PATCH 9/9] ARM: zylonite: " Lei Wen
2011-06-29  3:51             ` Lei Wen

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.