Gross hacks to make the Zylonite boot from flash in VGA.

Flash driver forward ported to 2.6.14

Index: linux-2.6.23/drivers/mtd/nand/Kconfig
===================================================================
--- linux-2.6.23.orig/drivers/mtd/nand/Kconfig	2007-10-09 21:31:38.000000000 +0100
+++ linux-2.6.23/drivers/mtd/nand/Kconfig	2008-02-13 00:59:45.000000000 +0000
@@ -223,6 +223,10 @@
 	tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
 	depends on ARCH_PXA
 
+config MTD_NAND_ZYLONITE
+	tristate "Support for NAND Flash on Zylonite"
+	depends on ARCH_PXA
+
 config MTD_NAND_BASLER_EXCITE
 	tristate  "Support for NAND Flash on Basler eXcite"
 	depends on BASLER_EXCITE
Index: linux-2.6.23/drivers/mtd/nand/Makefile
===================================================================
--- linux-2.6.23.orig/drivers/mtd/nand/Makefile	2007-10-09 21:31:38.000000000 +0100
+++ linux-2.6.23/drivers/mtd/nand/Makefile	2008-02-13 00:59:45.000000000 +0000
@@ -19,6 +19,7 @@
 obj-$(CONFIG_MTD_NAND_H1900)		+= h1910.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)	+= rtc_from4.o
 obj-$(CONFIG_MTD_NAND_SHARPSL)		+= sharpsl.o
+obj-$(CONFIG_MTD_NAND_ZYLONITE)		+= mhn_nand.o
 obj-$(CONFIG_MTD_NAND_TS7250)		+= ts7250.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)		+= nandsim.o
 obj-$(CONFIG_MTD_NAND_CS553X)		+= cs553x_nand.o
Index: linux-2.6.23/drivers/mtd/nand/mhn_nand.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.23/drivers/mtd/nand/mhn_nand.c	2008-02-13 00:59:45.000000000 +0000
@@ -0,0 +1,3869 @@
+/*
+ *  drivers/mtd/nand/mhn_nand.c
+ *
+ *  Copyright (C) 2005 Intel Coporation (chao.xie@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device on zylonite board
+ *   which utilizes the Samsung K9K1216Q0C parts. This is a 64Mibit NAND
+ *   flash device.
+
+ *(C) Copyright 2006 Marvell International Ltd.
+ * All Rights Reserved
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/dma.h>
+#include <asm/arch/mfp.h>
+//#include <asm/arch/cpu-freq-voltage-mhn.h>
+
+//#define NDCR 0xf0000000
+//#define NDCR        (*((volatile u32 *)0xf0000000))
+//#define NDCR              __REG_2(0x43100000)  /* Data Flash Control register */
+#define NDCR_SPARE_EN             (0x1<<31)
+#define NDCR_ECC_EN               (0x1<<30)
+#define NDCR_DMA_EN               (0x1<<29)
+#define NDCR_ND_RUN               (0x1<<28)
+#define NDCR_DWIDTH_C             (0x1<<27)
+#define NDCR_DWIDTH_M             (0x1<<26)
+#define NDCR_PAGE_SZ              (0x1<<24)
+#define NDCR_NCSX         (0x1<<23)
+#define NDCR_ND_MODE              (0x3<<21)
+#define NDCR_NAND_MODE    0x0
+#define NDCR_CLR_PG_CNT           (0x1<<20)
+#define NDCR_CLR_ECC      (       0x1<<19)
+#define NDCR_RD_ID_CNT_MASK       (0x7<<16)
+#define NDCR_RD_ID_CNT(x)       (((x) << 16) & NDCR_RD_ID_CNT_MASK)
+#define NDCR_RA_START     (0x1<<15)
+#define NDCR_PG_PER_BLK   (0x1<<14)
+#define NDCR_ND_ARB_EN    (0x1<<12)
+
+//#define NDSR        (*((volatile u32 *)0xf0000014))
+//#define NDSR              __REG_2(0x43100014)  /* Data Controller Status Register */
+#define NDSR_RDY  (0x1<<11)
+#define NDSR_CS0_PAGED    (0x1<<10)
+#define NDSR_CS1_PAGED    (0x1<<9)
+#define NDSR_CS0_CMDD     (0x1<<8)
+#define NDSR_CS1_CMDD     (0x1<<7)
+#define NDSR_CS0_BBD      (0x1<<6)
+#define NDSR_CS1_BBD      (0x1<<5)
+#define NDSR_DBERR        (0x1<<4)
+#define NDSR_SBERR        (0x1<<3)
+#define NDSR_WRDREQ       (0x1<<2)
+#define NDSR_RDDREQ       (0x1<<1)
+#define NDSR_WRCMDREQ     (0x1)
+
+#define OSCR              __REG(0x40A00010)  /* OS Timer Counter Register */
+//#define NDCB0             __REG_2(0x43100048)  /* Data Controller Command Buffer0 */
+//#define NDCB1             __REG_2(0x4310004C)  /* Data Controller Command Buffer1 */
+//#define NDCB2             __REG_2(0x43100050)  /* Data Controller Command Buffer2 */
+#define NDCB0_AUTO_RS             (0x1<<25)
+#define NDCB0_CSEL                (0x1<<24)
+#define NDCB0_CMD_TYPE_MASK       (0x7<<21)
+#define   NDCB0_CMD_TYPE(x)       (((x) << 21) & NDCB0_CMD_TYPE_MASK)
+#define NDCB0_NC          (0x1<<20)
+#define NDCB0_DBC         (0x1<<19)
+#define NDCB0_ADDR_CYC_MASK       (0x7<<16)
+#define   NDCB0_ADDR_CYC(x)       (((x) << 16) & NDCB0_ADDR_CYC_MASK)
+#define NDCB0_CMD2_MASK           (0xff<<8)
+#define NDCB0_CMD1_MASK           (0xff)
+#define NDCB0_ADDR_CYC_SHIFT      (16)
+#define DCMD0             __REG(0x4000020c)  /* DMA Command Address Register Channel 0 */
+#define DCMD1             __REG(0x4000021c)  /* DMA Command Address Register Channel 1 */
+#define DCMD2             __REG(0x4000022c)  /* DMA Command Address Register Channel 2 */
+#define DCMD3             __REG(0x4000023c)  /* DMA Command Address Register Channel 3 */
+#define DCMD4             __REG(0x4000024c)  /* DMA Command Address Register Channel 4 */
+#define DCMD5             __REG(0x4000025c)  /* DMA Command Address Register Channel 5 */
+#define DCMD6             __REG(0x4000026c)  /* DMA Command Address Register Channel 6 */
+#define DCMD7             __REG(0x4000027c)  /* DMA Command Address Register Channel 7 */
+#define DCMD8             __REG(0x4000028c)  /* DMA Command Address Register Channel 8 */
+#define DCMD9             __REG(0x4000029c)  /* DMA Command Address Register Channel 9 */
+#define DCMD10            __REG(0x400002ac)  /* DMA Command Address Register Channel 10 */
+#define DCMD11            __REG(0x400002bc)  /* DMA Command Address Register Channel 11 */
+#define DCMD12            __REG(0x400002cc)  /* DMA Command Address Register Channel 12 */
+#define DCMD13            __REG(0x400002dc)  /* DMA Command Address Register Channel 13 */
+#define DCMD14            __REG(0x400002ec)  /* DMA Command Address Register Channel 14 */
+#define DCMD15            __REG(0x400002fc)  /* DMA Command Address Register Channel 15 */
+#define DCMD(x)           __REG2(0x4000020c, (x) << 4)
+#define DCMD_INCSRCADDR   (1 << 31)       /* Source Address Increment Setting. */
+#define DCMD_INCTRGADDR   (1 << 30)       /* Target Address Increment Setting. */
+#define DCMD_FLOWSRC      (1 << 29)       /* Flow Control by the source. */
+#define DCMD_FLOWTRG      (1 << 28)       /* Flow Control by the target. */
+#define DCMD_STARTIRQEN   (1 << 22)       /* Start Interrupt Enable */
+#define DCMD_ENDIRQEN     (1 << 21)       /* End Interrupt Enable */
+#define DCMD_ENDIAN       (1 << 18)       /* Device Endian-ness. */
+#define DCMD_BURST8       (1 << 16)       /* 8 byte burst */
+#define DCMD_BURST16      (2 << 16)       /* 16 byte burst */
+#define DCMD_BURST32      (3 << 16)       /* 32 byte burst */
+#define DCMD_WIDTH1       (1 << 14)       /* 1 byte width */
+#define DCMD_WIDTH2       (2 << 14)       /* 2 byte width (HalfWord) */
+#define DCMD_WIDTH4       (3 << 14)       /* 4 byte width (Word) */
+#define DCMD_LENGTH       0x01fff         /* length mask (max = 8K - 1) */
+#define DCMD_RXPCDR       (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4)
+#define DCMD_RXMCDR       (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4)
+#define DCMD_TXPCDR       (DCMD_INCSRCADDR|DCMD_FLOWTRG|DCMD_BURST32|DCMD_WIDTH4)
+#define DRCMR(n)  __REG2(0x40000100, (n)<<2)
+#define DRCMR97           __REG(0x40001184)  /* Request to Channel Map Register for NAND interface data transmit & receive Request */
+#define DRCMR98           __REG(0x40001188)  /* Reserved */
+#define DRCMR99           __REG(0x4000118C)  /* Request to Channel Map Register for NAND interface command transmit Request */
+#define DRCMRRXSADR       DRCMR2
+#define DRCMRTXSADR       DRCMR3
+#define DRCMRRXBTRBR      DRCMR4
+#define DRCMRTXBTTHR      DRCMR5
+#define DRCMRRXFFRBR      DRCMR6
+#define DRCMRTXFFTHR      DRCMR7
+#define DRCMRRXMCDR       DRCMR8
+#define DRCMRRXMODR       DRCMR9
+#define DRCMRTXMODR       DRCMR10
+#define DRCMRRXPCDR       DRCMR11
+#define DRCMRTXPCDR       DRCMR12
+#define DRCMRRXSSDR       DRCMR13
+#define DRCMRTXSSDR       DRCMR14
+#define DRCMRRXICDR       DRCMR17
+#define DRCMRTXICDR       DRCMR18
+#define DRCMRRXSTRBR      DRCMR19
+#define DRCMRTXSTTHR      DRCMR20
+#define DRCMRRXMMC        DRCMR21
+#define DRCMRTXMMC        DRCMR22
+#define DRCMRRXMMC2       DRCMR93
+#define DRCMRTXMMC2       DRCMR94
+#define DRCMRRXMMC3       DRCMR100
+#define DRCMRTXMMC3       DRCMR101
+#define DRCMRUDC(x)       DRCMR((x) + 24)
+#define DRCMR_MAPVLD      (1 << 7)        /* Map Valid (read / write) */
+#define DRCMR_CHLNUM      0x1f            /* mask for Channel Number (read / write) */
+#define DCSR0             __REG(0x40000000)  /* DMA Control / Status Register for Channel 0 */
+#define DCSR1             __REG(0x40000004)  /* DMA Control / Status Register for Channel 1 */
+#define DCSR2             __REG(0x40000008)  /* DMA Control / Status Register for Channel 2 */
+#define DCSR3             __REG(0x4000000c)  /* DMA Control / Status Register for Channel 3 */
+#define DCSR4             __REG(0x40000010)  /* DMA Control / Status Register for Channel 4 */
+#define DCSR5             __REG(0x40000014)  /* DMA Control / Status Register for Channel 5 */
+#define DCSR6             __REG(0x40000018)  /* DMA Control / Status Register for Channel 6 */
+#define DCSR7             __REG(0x4000001c)  /* DMA Control / Status Register for Channel 7 */
+#define DCSR8             __REG(0x40000020)  /* DMA Control / Status Register for Channel 8 */
+#define DCSR9             __REG(0x40000024)  /* DMA Control / Status Register for Channel 9 */
+#define DCSR10            __REG(0x40000028)  /* DMA Control / Status Register for Channel 10 */
+#define DCSR11            __REG(0x4000002c)  /* DMA Control / Status Register for Channel 11 */
+#define DCSR12            __REG(0x40000030)  /* DMA Control / Status Register for Channel 12 */
+#define DCSR13            __REG(0x40000034)  /* DMA Control / Status Register for Channel 13 */
+#define DCSR14            __REG(0x40000038)  /* DMA Control / Status Register for Channel 14 */
+#define DCSR15            __REG(0x4000003c)  /* DMA Control / Status Register for Channel 15 */
+#define DCSR16            __REG(0x40000040)  /* DMA Control / Status Register for Channel 16 */
+#define DCSR17            __REG(0x40000044)  /* DMA Control / Status Register for Channel 17 */
+#define DCSR18            __REG(0x40000048)  /* DMA Control / Status Register for Channel 18 */
+#define DCSR19            __REG(0x4000004c)  /* DMA Control / Status Register for Channel 19 */
+#define DCSR20            __REG(0x40000050)  /* DMA Control / Status Register for Channel 20 */
+#define DCSR21            __REG(0x40000054)  /* DMA Control / Status Register for Channel 21 */
+#define DCSR22            __REG(0x40000058)  /* DMA Control / Status Register for Channel 22 */
+#define DCSR23            __REG(0x4000005c)  /* DMA Control / Status Register for Channel 23 */
+#define DCSR24            __REG(0x40000060)  /* DMA Control / Status Register for Channel 24 */
+#define DCSR25            __REG(0x40000064)  /* DMA Control / Status Register for Channel 25 */
+#define DCSR26            __REG(0x40000068)  /* DMA Control / Status Register for Channel 26 */
+#define DCSR27            __REG(0x4000006c)  /* DMA Control / Status Register for Channel 27 */
+#define DCSR28            __REG(0x40000070)  /* DMA Control / Status Register for Channel 28 */
+#define DCSR29            __REG(0x40000074)  /* DMA Control / Status Register for Channel 29 */
+#define DCSR30            __REG(0x40000078)  /* DMA Control / Status Register for Channel 30 */
+#define DCSR31            __REG(0x4000007c)  /* DMA Control / Status Register for Channel 31 */
+#define DCSR(x)           __REG2(0x40000000, (x) << 2)
+#define DCSR_RUN  (1 << 31)       /* Run Bit (read / write) */
+#define DCSR_NODESC       (1 << 30)       /* No-Descriptor Fetch (read / write) */
+#define DCSR_STOPIRQEN    (1 << 29)       /* Stop Interrupt Enable (read / write) */
+#define DCSR_EORIRQEN     (1 << 28)       /* End of Receive Interrupt Enable (R/W) */
+#define DCSR_EORJMPEN     (1 << 27)       /* Jump to next descriptor on EOR */
+#define DCSR_EORSTOPEN    (1 << 26)       /* STOP on an EOR */
+#define DCSR_SETCMPST     (1 << 25)       /* Set Descriptor Compare Status */
+#define DCSR_CLRCMPST     (1 << 24)       /* Clear Descriptor Compare Status */
+#define DCSR_CMPST        (1 << 10)       /* The Descriptor Compare Status */
+#define DCSR_EORINTR      (1 << 9)        /* The end of Receive */
+#define DCSR_REQPEND      (1 << 8)        /* Request Pending (read-only) */
+#define DCSR_RASINTR      (1 << 4)        /* Request After Channel Stopped */
+#define DCSR_STOPSTATE    (1 << 3)        /* Stop State (read-only) */
+#define DCSR_ENDINTR      (1 << 2)        /* End Interrupt (read / write) */
+#define DCSR_STARTINTR    (1 << 1)        /* Start Interrupt (read / write) */
+#define DCSR_BUSERR       (1 << 0)        /* Bus Error Interrupt (read / write) */
+#define DDADR(x)  __REG2(0x40000200, (x) << 4)
+//#define __REG_2(x)        (*((volatile u32 *)io_p2v_2(x)))
+#define IRQ_NAND      PXA_IRQ(45)
+#define   CKEN_NAND       4       ///< NAND Flash Controller Clock Enable
+
+/* #define CONFIG_MTD_NAND_MONAHANS_DEBUG */
+#ifdef CONFIG_MTD_NAND_MONAHANS_DEBUG
+#define D1(x) do { \
+		printk(KERN_DEBUG "%s: ", __FUNCTION__); \
+		x; \
+	}while(0)
+
+#define	DPRINTK(fmt,args...) printk(KERN_DEBUG fmt, ##args )
+#define PRINT_BUF(buf, num)	print_buf(buf, num)
+#else
+#define D1(x)
+#define DPRINTK(fmt,args...)
+#define PRINT_BUF(buf, num)
+#endif
+
+/* DFC timing 0 register */
+#define DFC_TIMING_tRP		0
+#define DFC_TIMING_tRH		3
+#define DFC_TIMING_tWP		8
+#define DFC_TIMING_tWH		11
+#define DFC_TIMING_tCS		16
+#define DFC_TIMING_tCH		19
+
+/* DFC timing 1 register */
+#define DFC_TIMING_tAR		0
+#define DFC_TIMING_tWHR		4
+#define DFC_TIMING_tR		16
+
+/* max value for each timing setting in DFC */
+#define DFC_TIMING_MAX_tCH	7
+#define DFC_TIMING_MAX_tCS	7
+#define DFC_TIMING_MAX_tWH	7
+#define DFC_TIMING_MAX_tWP	7
+#define DFC_TIMING_MAX_tRH	7
+#define DFC_TIMING_MAX_tRP	7
+#define DFC_TIMING_MAX_tR	65535
+#define DFC_TIMING_MAX_tWHR	15
+#define DFC_TIMING_MAX_tAR	15
+
+/*
+ * The Data Flash Controller Flash timing structure
+ * For NAND flash used on Zylonite board(Samsung K9K1216Q0C),
+ * user should use value at end of each row of following member
+ * bracketed.
+ */
+struct dfc_flash_timing {
+	uint32_t   tCH;	/* Enable signal hold time */
+	uint32_t   tCS;	/* Enable signal setup time */
+	uint32_t   tWH;	/* ND_nWE high duration */
+	uint32_t   tWP;	/* ND_nWE pulse time */
+	uint32_t   tRH;	/* ND_nRE high duration */
+	uint32_t   tRP;	/* ND_nRE pulse width */
+	uint32_t   tR;	/* ND_nWE high to ND_nRE low for read */
+	uint32_t   tWHR;/* ND_nWE high to ND_nRE low delay for status read */
+	uint32_t   tAR;	/* ND_ALE low to ND_nRE low delay */
+};
+
+/* DFC command type */
+enum {
+	DFC_CMD_READ		= 0x00000000,
+	DFC_CMD_PROGRAM		= 0x00200000,
+	DFC_CMD_ERASE		= 0x00400000,
+	DFC_CMD_READ_ID		= 0x00600000,
+	DFC_CMD_STATUS_READ	= 0x00800000,
+	DFC_CMD_RESET		= 0x00a00000
+};
+
+/*
+ * The Data Flash Controller Flash specification structure
+ * For NAND flash used on Zylonite board(Samsung K9K1216Q0C),
+ * user should use value at end of each row of following member
+ * bracketed.
+ */
+struct dfc_flash_info {
+	struct dfc_flash_timing timing; /* NAND Flash timing */
+
+	int	 enable_arbiter;/* Data flash bus arbiter enable (ND_ARB_EN) */
+	uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */
+	uint32_t row_addr_start;/* Row address start position (RA_START) */
+	uint32_t read_id_bytes;	/* returned ID bytes(RD_ID_CNT) */
+	uint32_t dfc_mode;	/* NAND, CARBONDALE, PIXLEY... (ND_MODE) */
+	uint32_t ncsx;		/* Chip select don't care bit (NCSX) */
+	uint32_t page_size;	/* Page size in bytes (PAGE_SZ) */
+	uint32_t oob_size;	/* OOB size */
+	uint32_t flash_width;	/* Width of Flash memory (DWIDTH_M) */
+	uint32_t dfc_width;	/* Width of flash controller(DWIDTH_C) */
+	uint32_t num_blocks;	/* Number of physical blocks in Flash */
+	uint32_t chip_id;
+
+	/* command codes */
+	uint32_t read1;		/* Read */
+	uint32_t read2;		/* unused, DFC don't support yet */
+	uint32_t program;	/* two cycle command */
+	uint32_t read_status;
+	uint32_t read_id;
+	uint32_t erase;		/* two cycle command */
+	uint32_t reset;
+	uint32_t lock;		/* lock whole flash */
+	uint32_t unlock;	/* two cycle command, supporting partial unlock */
+	uint32_t lock_status;	/* read block lock status */
+
+	/* addr2ndcb1 - encode address cycles into register NDCB1 */
+	/* ndbbr2addr - convert register NDBBR to bad block address */
+	int (*addr2ndcb1)(uint16_t cmd, uint32_t addr, uint32_t *p);
+	int (*ndbbr2addr)(uint16_t cmd, uint32_t ndbbr,uint32_t *p);
+};
+
+enum {
+	DFC_FLASH_NULL = 0 ,
+	DFC_FLASH_Samsung_512Mb_X_16 = 1,
+	DFC_FLASH_Micron_1Gb_X_8 = 2,
+	DFC_FLASH_Micron_1Gb_X_16 = 3,
+	DFC_FLASH_STM_1Gb_X_16 = 4,
+	DFC_FLASH_STM_2Gb_X_16 = 5,
+	DFC_FLASH_END,
+};
+
+static int dfc_get_flash_info(int type, struct dfc_flash_info **flash_info);
+
+#define		DFC_NDCR	0
+#define		DFC_NDTR0CS0	1
+#define		DFC_NDTR1CS0	3
+#define		DFC_NDSR	5
+#define		DFC_NDPCR	6
+#define		DFC_NDBDR0	7
+#define		DFC_NDBDR1	8
+#define		DFC_NDDB	16
+#define		DFC_NDCB0	18
+#define		DFC_NDCB1	19
+#define		DFC_NDCB2	20
+
+/* The Data Flash Controller Mode structure */
+struct dfc_mode {
+	int   enable_dma;	/* DMA, or nonDMA mode */
+	int   enable_ecc;	/* ECC on/off */
+	int   enable_spare;	/* Spare enable */
+	int   chip_select;	/* CS0 or CS1 */
+};
+
+/* The Data Flash Controller Context structure */
+struct dfc_context {
+	unsigned char __iomem	*membase;	/* DFC register base */
+	struct dfc_mode 	*dfc_mode;	/* DFC mode */
+	int 			data_dma_ch;	/* Data DMA channel number */
+	int 			cmd_dma_ch;	/* CMD  DMA channel number */
+	struct dfc_flash_info 	*flash_info; /* Flash Spec */
+	struct mtd_info 	*mtd;
+};
+
+#define NDCB0_DMA_ADDR	0x43100048
+#define NDDB_DMA_ADDR	0x43100040
+
+#define NDSR_MASK	0xFFF
+
+/* The following data is a rough evaluation */
+
+/* microsecond, for readID/readStatus/reset */
+#define NAND_OTHER_TIMEOUT 		10
+/* microsecond, for readID/readStatus/reset */
+#define NAND_CMD_TIMEOUT		10
+
+#define BBT_BLOCK_BAD	0x03
+#define BBT_BLOCK_GOOD	0x00
+#define BBT_BLOCK_REV1	0x01
+#define BBT_BLOCK_REV2	0x02
+
+#define BUFLEN		(2048 + 64)
+
+/*
+ * DFC data size enumeration transfered from/to controller,
+ * including padding (zero)to be a multiple of 32.
+ */
+enum {
+	DFC_DATA_SIZE_STATUS = 8,	/* ReadStatus/ReadBlockLockStatus */
+	DFC_DATA_SIZE_ID = 7,	/* ReadID */
+
+	DFC_DATA_SIZE_32 = 32,
+	DFC_DATA_SIZE_512 = 512,	/* R/W disabling spare area */
+	DFC_DATA_SIZE_520 = 520,	/* Spare=1, ECC=1 */
+	DFC_DATA_SIZE_528 = 528,	/* Spare=1, ECC=0 */
+	DFC_DATA_SIZE_544 = 544,	/* R/W enabling spare area.(DMA mode)*/
+
+	DFC_DATA_SIZE_64 = 64,
+	DFC_DATA_SIZE_2048 = 2048, 	/* R/W disabling spare area */
+	DFC_DATA_SIZE_2088 = 2088,	/* R/W enabling spare area with ecc */
+	DFC_DATA_SIZE_2112 = 2112,	/* R/W enabling spare area without ecc*/
+	DFC_DATA_SIZE_2096 = 2096,	/* R/W enabling spare area */
+	DFC_DATA_SIZE_UNUSED = 0xFFFF
+};
+
+/* DFC padding size enumeration transfered from/to controller */
+enum {
+	/*
+	 * ReadStatus/ReadBlockLockStatus/ReadID/
+	 * Read/Program disabling spare area(Both 512 and 2048)
+	 * Read/Program enabling spare area, disabling ECC
+	 */
+	DFC_PADDING_SIZE_0 = 0,
+
+	/* Read/program with SPARE_EN=1, ECC_EN=0, pgSize=512 */
+	DFC_PADDING_SIZE_16 = 16,
+	/* for read/program with SPARE_EN=1, ECC_EN=1, pgSize=512 and 2048 */
+	DFC_PADDING_SIZE_24 = 24,
+	DFC_PADDING_SIZE_UNUSED = 0xFFFF
+};
+
+static unsigned int flash_config = DFC_FLASH_NULL;
+
+void dfc_set_timing(struct dfc_context *context, struct dfc_flash_timing *t);
+void dfc_set_dma(struct dfc_context *context);
+void dfc_set_ecc(struct dfc_context *context);
+void dfc_set_spare(struct dfc_context *context);
+
+int dfc_get_pattern(struct dfc_context *context, uint16_t cmd,
+			int *data_size, int *padding);
+
+static int dfc_wait_event(struct dfc_context *context, uint32_t event,
+		uint32_t *event_out, uint32_t timeout, int enable_int);
+
+int dfc_send_cmd(struct dfc_context *context, uint16_t cmd,
+		uint32_t addr, int num_pages);
+
+void dfc_stop(struct dfc_context *context);
+void dfc_read_fifo_partial(struct dfc_context *context, uint8_t *buffer,
+			int nbytes, int data_size);
+void dfc_write_fifo_partial(struct dfc_context *context, uint8_t *buffer,
+			int nbytes, int data_size);
+
+void dfc_read_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes);
+void dfc_write_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes);
+
+void dfc_read_badblock_addr(struct dfc_context *context, uint32_t *bbaddr);
+
+void dfc_clear_int(struct dfc_context *context, uint32_t int_mask);
+void dfc_enable_int(struct dfc_context *context, uint32_t int_mask);
+void dfc_disable_int(struct dfc_context *context, uint32_t int_mask);
+
+/* high level primitives */
+int dfc_init(struct dfc_context *context, int type);
+int dfc_init_no_gpio(struct dfc_context *context, int type);
+
+int dfc_reset_flash(struct dfc_context *context);
+
+int dfc_setup_cmd_dma(struct dfc_context *context,
+		uint16_t cmd, uint32_t addr, int num_pages,
+		uint32_t *buf, uint32_t buf_phys,
+		uint32_t next_desc_phys, uint32_t dma_int_en,
+		struct pxa_dma_desc *dma_desc);
+
+int dfc_setup_data_dma(struct dfc_context *context,
+		uint16_t cmd, uint32_t buf_phys,
+		uint32_t next_desc_phys, uint32_t dma_int_en,
+		struct pxa_dma_desc *dma_desc);
+
+void dfc_start_cmd_dma(struct dfc_context *context,
+			struct pxa_dma_desc *dma_desc);
+void dfc_start_data_dma(struct dfc_context *context,
+			struct pxa_dma_desc *dma_desc);
+static int monahans_df_dev_ready(struct mtd_info *mtd);
+
+#ifdef CONFIG_DVFM
+static int mhn_nand_dvfm_notifier(unsigned cmd, void *client_data, void *info);
+static struct mhn_fv_notifier dvfm_notifier = {
+	.name 		= "monahans-nand-flash",
+	.priority 	= 0,
+	.notifier_call 	= mhn_nand_dvfm_notifier,
+};
+#endif
+
+static unsigned short search_rel_block(int block, struct mtd_info *mtd);
+
+/*****************************************************************************
+ * The DFC registers read/write routines
+ *****************************************************************************/
+static inline void dfc_write(struct dfc_context *context, int offset,
+		unsigned long value)
+{
+	offset <<= 2;
+	writel(value, context->membase + offset);
+}
+
+static inline unsigned int dfc_read(struct dfc_context *context, int offset)
+{
+	offset <<= 2;
+	return __raw_readl(context->membase + offset);
+}
+
+/****************************************************************************
+ * Flash Information
+ ***************************************************************************/
+
+static int Samsung512MbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int Samsung512MbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info samsung512MbX16 =
+{
+	.timing = {
+		.tCH = 10,	/* tCH, Enable signal hold time */
+		.tCS = 0,	/* tCS, Enable signal setup time */
+		.tWH = 20,	/* tWH, ND_nWE high duration */
+		.tWP = 40,	/* tWP, ND_nWE pulse time */
+		.tRH = 30,	/* tRH, ND_nRE high duration */
+		.tRP = 40,	/* tRP, ND_nRE pulse width */
+		/* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+		.tR = 11123,
+		/* tWHR, ND_nWE high to ND_nRE low delay for status read */
+		.tWHR = 110,
+		.tAR = 10,	/* tAR, ND_ALE low to ND_nRE low delay */
+	},
+	.enable_arbiter = 1,	/* Data flash bus arbiter enable */
+	.page_per_block = 32,	/* Pages per block */
+	.row_addr_start = 0,	/* Second cycle start, Row address start position */
+	.read_id_bytes = 2,	/* 2 bytes, returned ID bytes */
+	.dfc_mode = 0,		/* NAND mode */
+	.ncsx = 0,
+	.page_size = 512,	/* Page size in bytes */
+	.oob_size = 16,		/* OOB size in bytes */
+	.flash_width = 16,	/* Width of Flash memory */
+	.dfc_width = 16,	/* Width of flash controller */
+	.num_blocks = 4096,	/* Number of physical blocks in Flash */
+	.chip_id =  0x46ec,
+
+	/* command codes */
+	.read1 = 0x0000,	/* Read */
+	.read2 = 0x0050,	/* Read1 unused, current DFC don't support */
+	.program = 0x1080,	/* Write, two cycle command */
+	.read_status = 0x0070,	/* Read status */
+	.read_id = 0x0090,	/* Read ID */
+	.erase =  0xD060,	/* Erase, two cycle command */
+	.reset = 0x00FF,	/* Reset */
+	.lock = 0x002A,		/* Lock whole flash */
+	.unlock = 0x2423,	/* Unlock, two cycle command, supporting partial unlock */
+	.lock_status = 0x007A,	/* Read block lock status */
+	.addr2ndcb1 = Samsung512MbX16Addr2NDCB1,
+	.ndbbr2addr = Samsung512MbX16NDBBR2Addr,
+};
+
+static int Samsung512MbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+	uint32_t ndcb1 = 0;
+
+	if (addr >= 0x4000000) return -EINVAL;
+
+	if (cmd == samsung512MbX16.read1 || cmd == samsung512MbX16.program) {
+		ndcb1 = (addr & 0xFF) | ((addr >> 1) & 0x01FFFF00);
+	} else if (cmd == samsung512MbX16.erase) {
+		ndcb1 = ((addr >> 9) & 0x00FFFFFF);
+	}
+
+	*p = ndcb1;
+	return 0;
+
+}
+
+static int Samsung512MbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+	*p = ndbbr << 9;
+	return 0;
+}
+
+static int Micron1GbX8Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int Micron1GbX8NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info micron1GbX8 =
+{
+	.timing = {
+		.tCH = 10,	/* tCH, Enable signal hold time */
+		.tCS = 25,	/* tCS, Enable signal setup time */
+		.tWH = 15,	/* tWH, ND_nWE high duration */
+		.tWP = 25,	/* tWP, ND_nWE pulse time */
+		.tRH = 15,	/* tRH, ND_nRE high duration */
+		.tRP = 25,	/* tRP, ND_nRE pulse width */
+		/* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+		.tR = 25000,
+		/* tWHR, ND_nWE high to ND_nRE low delay for status read */
+		.tWHR = 60,
+		.tAR = 10,	/* tAR, ND_ALE low to ND_nRE low delay */
+	},
+	.enable_arbiter = 1,	/* Data flash bus arbiter enable */
+	.page_per_block = 64,	/* Pages per block */
+	.row_addr_start = 1,	/* Second cycle start, Row address start position */
+	.read_id_bytes = 4,	/* Returned ID bytes */
+	.dfc_mode = 0,		/* NAND mode */
+	.ncsx = 0,
+	.page_size = 2048,	/* Page size in bytes */
+	.oob_size = 64,		/* OOB size in bytes */
+	.flash_width = 8,	/* Width of Flash memory */
+	.dfc_width = 8,		/* Width of flash controller */
+	.num_blocks = 1024,	/* Number of physical blocks in Flash */
+	.chip_id =  0xa12c,
+	/* command codes */
+	.read1 = 0x3000,	/* Read */
+	.read2 = 0x0050,	/* Read1 unused, current DFC don't support */
+	.program = 0x1080,	/* Write, two cycle command */
+	.read_status = 0x0070,	/* Read status */
+	.read_id = 0x0090,	/* Read ID */
+	.erase =  0xD060,	/* Erase, two cycle command */
+	.reset = 0x00FF,	/* Reset */
+	.lock = 0x002A,		/* Lock whole flash */
+	.unlock = 0x2423,	/* Unlock, two cycle command, supporting partial unlock */
+	.lock_status = 0x007A,	/* Read block lock status */
+	.addr2ndcb1 = Micron1GbX8Addr2NDCB1,
+	.ndbbr2addr = Micron1GbX8NDBBR2Addr,
+};
+
+static int Micron1GbX8Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+	uint32_t ndcb1 = 0;
+	uint32_t page;
+
+	if (addr >= 0x8000000)
+		return -EINVAL;
+	page = addr / micron1GbX8.page_size;
+	addr =  (page / micron1GbX8.page_per_block) << 18 |
+		(page % micron1GbX8.page_per_block) << 12;
+
+	if (cmd == micron1GbX8.read1 || cmd == micron1GbX8.program) {
+		ndcb1 = (addr & 0xFFF) | ((addr << 4) & 0xFFFF0000);
+	}
+	else if (cmd == micron1GbX8.erase) {
+		ndcb1 = ((addr >> 18) << 6) & 0xFFFF;
+	}
+
+	*p = ndcb1;
+	return 0;
+}
+
+static int Micron1GbX8NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+	if (cmd == micron1GbX8.read1 || cmd == micron1GbX8.program) {
+		*p = ((ndbbr & 0xF) << 8) | ((ndbbr >> 8) << 16);
+	}
+	else if (cmd == micron1GbX8.erase) {
+		*p = (ndbbr >> 6) << 18;
+	}
+
+
+	return 0;
+}
+
+
+static int Micron1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int Micron1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info micron1GbX16 =
+{
+	.timing = {
+		.tCH = 10,	/* tCH, Enable signal hold time */
+		.tCS = 25,	/* tCS, Enable signal setup time */
+		.tWH = 15,	/* tWH, ND_nWE high duration */
+		.tWP = 25,	/* tWP, ND_nWE pulse time */
+		.tRH = 15,	/* tRH, ND_nRE high duration */
+		.tRP = 25,	/* tRP, ND_nRE pulse width */
+		/* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+		.tR = 25000,
+		/* tWHR, ND_nWE high to ND_nRE low delay for status read */
+		.tWHR = 60,
+		.tAR = 10,	/* tAR, ND_ALE low to ND_nRE low delay */
+	},
+	.enable_arbiter = 1,	/* Data flash bus arbiter enable */
+	.page_per_block = 64,	/* Pages per block */
+	.row_addr_start = 1,	/* Second cycle start, Row address start position */
+	.read_id_bytes = 4,	/* Returned ID bytes */
+	.dfc_mode = 0,		/* NAND mode */
+	.ncsx = 0,
+	.page_size = 2048,	/* Page size in bytes */
+	.oob_size = 64,		/* OOB size in bytes */
+	.flash_width = 16,	/* Width of Flash memory */
+	.dfc_width = 16,	/* Width of flash controller */
+	.num_blocks = 1024,	/* Number of physical blocks in Flash */
+	.chip_id =  0xb12c,
+
+	/* command codes */
+	.read1 = 0x3000,	/* Read */
+	.read2 = 0x0050,	/* Read1 unused, current DFC don't support */
+	.program = 0x1080,	/* Write, two cycle command */
+	.read_status = 0x0070,	/* Read status */
+	.read_id = 0x0090,	/* Read ID */
+	.erase =  0xD060,	/* Erase, two cycle command */
+	.reset = 0x00FF,	/* Reset */
+	.lock = 0x002A,		/* Lock whole flash */
+	.unlock = 0x2423,	/* Unlock, two cycle command, supporting partial unlock */
+	.lock_status = 0x007A,	/* Read block lock status */
+	.addr2ndcb1 = Micron1GbX16Addr2NDCB1,
+	.ndbbr2addr = Micron1GbX16NDBBR2Addr,
+};
+
+static int Micron1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+	uint32_t ndcb1 = 0;
+	uint32_t page;
+
+	if (addr >= 0x8000000)
+		return -EINVAL;
+	page = addr / micron1GbX16.page_size;
+	addr =  (page / micron1GbX16.page_per_block) << 17 |
+		(page % micron1GbX16.page_per_block) << 11;
+
+	if (cmd == micron1GbX16.read1 || cmd == micron1GbX16.program) {
+		ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
+	}
+	else if (cmd == micron1GbX16.erase) {
+		ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
+	}
+	*p = ndcb1;
+	return 0;
+}
+
+static int Micron1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+	if (cmd == micron1GbX16.read1 || cmd == micron1GbX16.program) {
+		*p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
+	}
+	else if (cmd == micron1GbX16.erase) {
+		*p = (ndbbr >> 6) << 17;
+	}
+
+	return 0;
+}
+
+static int STM1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int STM1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info stm1GbX16 =
+{
+	.timing = {
+		.tCH = 10,	/* tCH, Enable signal hold time */
+		.tCS = 10,	/* tCS, Enable signal setup time */
+		.tWH = 20,	/* tWH, ND_nWE high duration */
+		.tWP = 25,	/* tWP, ND_nWE pulse time */
+		.tRH = 20,	/* tRH, ND_nRE high duration */
+		.tRP = 25,	/* tRP, ND_nRE pulse width */
+		/* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+		.tR = 25000,
+		/* tWHR, ND_nWE high to ND_nRE low delay for status read */
+		.tWHR = 60,
+		.tAR = 10,	/* tAR, ND_ALE low to ND_nRE low delay */
+	},
+	.enable_arbiter = 1,	/* Data flash bus arbiter enable */
+	.page_per_block = 64,	/* Pages per block */
+	.row_addr_start = 1,	/* Second cycle start, Row address start position */
+	.read_id_bytes = 4,	/* Returned ID bytes */
+	.dfc_mode = 0,		/* NAND mode */
+	.ncsx = 0,
+	.page_size = 2048,	/* Page size in bytes */
+	.oob_size = 64,		/* OOB size in bytes */
+	.flash_width = 16,	/* Width of Flash memory */
+	.dfc_width = 16,	/* Width of flash controller */
+	.num_blocks = 1024,	/* Number of physical blocks in Flash */
+	.chip_id =  0xb120,
+
+	/* command codes */
+	.read1 = 0x3000,	/* Read */
+	.read2 = 0x0050,	/* Read1 unused, current DFC don't support */
+	.program = 0x1080,	/* Write, two cycle command */
+	.read_status = 0x0070,	/* Read status */
+	.read_id = 0x0090,	/* Read ID */
+	.erase =  0xD060,	/* Erase, two cycle command */
+	.reset = 0x00FF,	/* Reset */
+	.lock = 0x002A,		/* Lock whole flash */
+	.unlock = 0x2423,	/* Unlock, two cycle command, supporting partial unlock */
+	.lock_status = 0x007A,	/* Read block lock status */
+	.addr2ndcb1 = STM1GbX16Addr2NDCB1,
+	.ndbbr2addr = STM1GbX16NDBBR2Addr,
+};
+
+static int STM1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+	uint32_t ndcb1 = 0;
+	uint32_t page;
+
+	if (addr >= 0x8000000)
+		return -EINVAL;
+	page = addr / stm1GbX16.page_size;
+	addr =  (page / stm1GbX16.page_per_block) << 17 |
+		(page % stm1GbX16.page_per_block) << 11;
+
+	if (cmd == stm1GbX16.read1 || cmd == stm1GbX16.program) {
+		ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
+	}
+	else if (cmd == stm1GbX16.erase) {
+		ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
+	}
+	*p = ndcb1;
+	return 0;
+}
+
+static int STM1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+	if (cmd == stm1GbX16.read1 || cmd == stm1GbX16.program) {
+		*p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
+	}
+	else if (cmd == stm1GbX16.erase) {
+		*p = (ndbbr >> 6) << 17;
+	}
+
+	return 0;
+}
+
+static int STM2GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
+static int STM2GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
+
+static struct dfc_flash_info stm2GbX16 =
+{
+	.timing = {
+		.tCH = 10,      /* tCH, Enable signal hold time */
+		.tCS = 10,      /* tCS, Enable signal setup time */
+		.tWH = 20,      /* tWH, ND_nWE high duration */
+		.tWP = 25,      /* tWP, ND_nWE pulse time */
+		.tRH = 20,      /* tRH, ND_nRE high duration */
+		.tRP = 25,      /* tRP, ND_nRE pulse width */
+		/* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
+		.tR = 25000,
+		/* tWHR, ND_nWE high to ND_nRE low delay for status read */
+		.tWHR = 60,
+		.tAR = 10,      /* tAR, ND_ALE low to ND_nRE low delay */
+	},
+	.enable_arbiter = 1,    /* Data flash bus arbiter enable */
+	.page_per_block = 64,   /* Pages per block */
+	.row_addr_start = 1,	/* Second cycle start, Row address start position */
+	.read_id_bytes = 4,     /* Returned ID bytes */
+	.dfc_mode = 0,          /* NAND mode */
+	.ncsx = 0,
+	.page_size = 2048,      /* Page size in bytes */
+	.oob_size = 64,         /* OOB size in bytes */
+	.flash_width = 16,      /* Width of Flash memory */
+	.dfc_width = 16,        /* Width of flash controller */
+	.num_blocks = 2048,     /* Number of physical blocks in Flash */
+	.chip_id =  0xca20,
+
+	/* command codes */
+	.read1 = 0x3000,        /* Read */
+	.read2 = 0x0050,        /* Read1 unused, current DFC don't support */
+	.program = 0x1080,      /* Write, two cycle command */
+	.read_status = 0x0070,  /* Read status */
+	.read_id = 0x0090,      /* Read ID */
+	.erase =  0xD060,       /* Erase, two cycle command */
+	.reset = 0x00FF,        /* Reset */
+	.lock = 0x002A,         /* Lock whole flash */
+	.unlock = 0x2423,	/* Unlock, two cycle command, supporting partial unlock */
+	.lock_status = 0x007A,  /* Read block lock status */
+	.addr2ndcb1 = STM2GbX16Addr2NDCB1,
+	.ndbbr2addr = STM2GbX16NDBBR2Addr,
+};
+
+static int STM2GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
+{
+	uint32_t ndcb1 = 0;
+	uint32_t page;
+
+	if (addr >= 0x8000000)
+		return -EINVAL;
+	page = addr / stm2GbX16.page_size;
+	addr =  (page / stm2GbX16.page_per_block) << 17 |
+		(page % stm2GbX16.page_per_block) << 11;
+
+	if (cmd == stm2GbX16.read1 || cmd == stm2GbX16.program) {
+		ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
+	}
+	else if (cmd == stm2GbX16.erase) {
+		ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
+	}
+	*p = ndcb1;
+	return 0;
+}
+
+static int STM2GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
+{
+	if (cmd == stm2GbX16.read1 || cmd == stm2GbX16.program) {
+		*p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
+	}
+	else if (cmd == stm2GbX16.erase) {
+		*p = (ndbbr >> 6) << 17;
+	}
+
+	return 0;
+}
+
+static struct {
+	int type;
+	struct dfc_flash_info *flash_info;
+} type_info[] = {
+	{ DFC_FLASH_Samsung_512Mb_X_16, &samsung512MbX16},
+	{ DFC_FLASH_Micron_1Gb_X_8, &micron1GbX8},
+	{ DFC_FLASH_Micron_1Gb_X_16, &micron1GbX16},
+	{ DFC_FLASH_STM_1Gb_X_16, &stm1GbX16},
+	{ DFC_FLASH_STM_2Gb_X_16, &stm2GbX16},
+	{ DFC_FLASH_NULL, NULL},
+};
+
+int dfc_get_flash_info(int type, struct dfc_flash_info **flash_info)
+{
+	uint32_t i = 0;
+
+	while(type_info[i].type != DFC_FLASH_NULL) {
+		if (type_info[i].type == type) {
+			*flash_info = type_info[i].flash_info;
+			return 0;
+		}
+		i++;
+	}
+	*flash_info = NULL;
+	return -EINVAL;
+}
+
+/******************************************************************************
+  dfc_set_timing
+
+  Description:
+	This function sets flash timing property in DFC timing register
+	according to input timing value embodied in context structure.
+	It is called once during the hardware initialization.
+  Input Parameters:
+  Output Parameters:
+	None
+  Returns:
+	None
+*******************************************************************************/
+//#if defined(CONFIG_CPU_MONAHANS_L) || defined(CONFIG_CPU_MONAHANS_LV)
+#define DFC_CLOCK	208
+//#else
+//#define DFC_CLOCK	104
+//#endif
+#define CLOCK_NS	DFC_CLOCK/1000
+
+void dfc_set_timing(struct dfc_context *context, struct dfc_flash_timing *t)
+{
+	struct dfc_flash_timing timing = *t;
+
+	uint32_t  r0 = 0;
+	uint32_t  r1 = 0;
+
+	/*
+   	 * num of clock cycles = time (ns) / one clock sycle (ns) + 1
+   	 * - integer division will truncate the result, so add a 1 in all cases
+   	 * - subtract the extra 1 cycle added to all register timing values
+   	 */
+   	timing.tCH = min(((int) (timing.tCH * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tCH);
+   	timing.tCS = min(((int) (timing.tCS * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tCS);
+   	timing.tWH = min(((int) (timing.tWH * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tWH);
+   	timing.tWP = min(((int) (timing.tWP * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tWP);
+   	timing.tRH = min(((int) (timing.tRH * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tRH);
+   	timing.tRP = min(((int) (timing.tRP * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tRP);
+
+   	r0 = (timing.tCH << DFC_TIMING_tCH) |
+   	     (timing.tCS << DFC_TIMING_tCS) |
+   	     (timing.tWH << DFC_TIMING_tWH) |
+   	     (timing.tWP << DFC_TIMING_tWP) |
+   	     (timing.tRH << DFC_TIMING_tRH) |
+   	     (timing.tRP << DFC_TIMING_tRP);
+
+	dfc_write(context, DFC_NDTR0CS0, r0);
+
+   	timing.tR   = min(((int) (timing.tR   * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tR);
+   	timing.tWHR = min(((int) (timing.tWHR * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tWHR);
+   	timing.tAR  = min(((int) (timing.tAR  * CLOCK_NS) + 1),
+   			DFC_TIMING_MAX_tAR);
+
+   	r1 = (timing.tR   << DFC_TIMING_tR)   |
+   	     (timing.tWHR << DFC_TIMING_tWHR) |
+   	     (timing.tAR  << DFC_TIMING_tAR);
+
+	dfc_write(context, DFC_NDTR1CS0, r1);
+   	return;
+}
+
+/******************************************************************************
+  dfc_set_dma
+
+  Description:
+		Enables or Disables DMA in line with setting in DFC mode of context
+		structure. DMA mode of DFC. Performs a read-modify-write operation that
+		only changes the driven DMA_EN bit field In DMA mode, all commands and
+		data are transferred by DMA.  DMA can be enable/disable on the fly.
+  Input Parameters:
+	context -Pointer to DFC context structure
+  	Output Parameters:
+		None
+	Returns:
+		None
+*******************************************************************************/
+void
+dfc_set_dma(struct dfc_context* context)
+{
+	uint32_t ndcr;
+
+	ndcr = dfc_read(context, DFC_NDCR);
+	if (context->dfc_mode->enable_dma)
+		ndcr |= NDCR_DMA_EN;
+	else
+		ndcr &= ~NDCR_DMA_EN;
+
+	dfc_write(context, DFC_NDCR, ndcr);
+
+	/* Read again to make sure write work */
+	ndcr = dfc_read(context, DFC_NDCR);
+	return;
+}
+
+
+/******************************************************************************
+  dfc_set_ecc
+
+  Description:
+		This function enables or disables hardware ECC capability of DFC in line
+		with setting in DFC mode of context structure.
+  Input Parameters:
+	context -Pointer to DFC context structure
+	Output Parameters:
+		None
+	Returns:
+		None
+*******************************************************************************/
+void
+dfc_set_ecc(struct dfc_context* context)
+{
+	uint32_t ndcr;
+
+	ndcr = dfc_read(context, DFC_NDCR);
+	if (context->dfc_mode->enable_ecc)
+		ndcr |= NDCR_ECC_EN;
+	else
+		ndcr &= ~NDCR_ECC_EN;
+
+	dfc_write(context, DFC_NDCR, ndcr);
+
+	/* Read again to make sure write work */
+	ndcr = dfc_read(context, DFC_NDCR);
+	return;
+}
+
+/******************************************************************************
+  dfc_set_spare
+
+  Description:
+		This function enables or disables accesses to spare area of NAND Flash
+		through DFC in line with setting in DFC mode of context structure.
+  Input Parameters:
+	context -Pointer to DFC context structure
+  Output Parameters:
+	None
+  Returns:
+	None
+*******************************************************************************/
+void
+dfc_set_spare(struct dfc_context* context)
+{
+	uint32_t ndcr;
+
+	ndcr = dfc_read(context, DFC_NDCR);
+	if (context->dfc_mode->enable_spare)
+		ndcr |= NDCR_SPARE_EN;
+	else
+		ndcr &= ~NDCR_SPARE_EN;
+
+	dfc_write(context, DFC_NDCR, ndcr);
+
+	/* Read again to make sure write work */
+	ndcr = dfc_read(context, DFC_NDCR);
+	return;
+}
+
+static unsigned int get_delta (unsigned int start)
+{
+    unsigned int stop = OSCR;
+    return (stop - start);
+}
+
+static int dfc_wait_event(struct dfc_context *context, uint32_t event,
+		uint32_t *event_out, uint32_t timeout, int enable_int)
+{
+	uint32_t ndsr;
+	uint32_t to = 3 * timeout;	/* 3 ticks ~ 1us */
+	int status;
+	int start = OSCR;
+
+	if (enable_int)
+		dfc_enable_int(context, event);
+
+	while (1) {
+		ndsr = dfc_read(context, DFC_NDSR);
+		ndsr &= NDSR_MASK;
+		if (ndsr & event) {
+			/* event happened */
+			*event_out = ndsr & event;
+			dfc_clear_int(context, *event_out);
+			status = 0;
+			break;
+		} else if (get_delta(start) > to) {
+			status = -ETIME;
+			break;
+		}
+	}
+
+	if (enable_int)
+		dfc_disable_int(context, event);
+	return status;
+}
+
+/******************************************************************************
+  dfc_get_pattern
+
+  Description:
+	This function is used to retrieve buffer size setting for a transaction
+	based on cmd.
+  Input Parameters:
+	context - Pointer to DFC context structure
+	cmd
+	  Specifies type of command to be sent to NAND flash .The LSB of this
+	  parameter defines the first command code for 2-cycles command. The
+	  MSB defines the second command code for 2-cycles command. If MSB is
+	  set to zero, this indicates that one cycle command
+	Output Parameters:
+	data_size
+	  It is used to retrieve  length of data transferred to/from DFC,
+	  which includes padding bytes
+	padding
+	  It is used to retrieve how many padding bytes there should be
+	  in buffer of data_size.
+	Returns:
+	0
+	  If size setting is returned successfully
+	-EINVAL
+	  If page size specified in flash spec of context structure is not 512 or
+	  2048;If specified command index is not read1/program/erase/reset/readID/
+	  readStatus.
+*******************************************************************************/
+int dfc_get_pattern(struct dfc_context *context, uint16_t cmd,
+			int *data_size, int *padding)
+{
+	struct dfc_mode* dfc_mode = context->dfc_mode;
+	struct dfc_flash_info * flash_info = context->flash_info;
+	uint32_t page_size = context->flash_info->page_size; /* 512 or 2048 */
+
+	if (cmd == flash_info->read1 ||
+		cmd == flash_info->program) {
+		if (512 == page_size) {
+			/* add for DMA */
+			if (dfc_mode->enable_dma) {
+				*data_size = DFC_DATA_SIZE_544;
+				if (dfc_mode->enable_ecc)
+					*padding = DFC_PADDING_SIZE_24;
+				else
+					*padding = DFC_PADDING_SIZE_16;
+			} else if (!dfc_mode->enable_spare) {
+				*data_size = DFC_DATA_SIZE_512;
+				*padding = DFC_PADDING_SIZE_0;
+			} else {
+
+				if (dfc_mode->enable_ecc)
+					*data_size = DFC_DATA_SIZE_520;
+				else
+					*data_size = DFC_DATA_SIZE_528;
+
+				*padding = DFC_PADDING_SIZE_0;
+			}
+		} else if (2048 == page_size) {
+			/* add for DMA */
+			if (dfc_mode->enable_dma) {
+				*data_size = DFC_DATA_SIZE_2112;
+				if (dfc_mode->enable_ecc)
+					*padding = DFC_PADDING_SIZE_24;
+				else
+					*padding = DFC_PADDING_SIZE_0;
+			} else if (!dfc_mode->enable_spare) {
+				*data_size = DFC_DATA_SIZE_2048;
+				*padding = DFC_PADDING_SIZE_0;
+			} else {
+
+				if (dfc_mode->enable_ecc)
+					*data_size = DFC_DATA_SIZE_2088;
+				else
+					*data_size = DFC_DATA_SIZE_2112;
+
+				*padding = DFC_PADDING_SIZE_0;
+			}
+		} else /* if the page_size is neither 512 or 2048 */
+			return -EINVAL;
+	} else if (cmd == flash_info->read_id) {
+		*data_size = DFC_DATA_SIZE_ID;
+		*padding = DFC_PADDING_SIZE_0;
+	} else if(cmd == flash_info->read_status) {
+		*data_size = DFC_DATA_SIZE_STATUS;
+		*padding = DFC_PADDING_SIZE_0;
+	} else if (cmd == flash_info->erase || cmd == flash_info->reset) {
+		*data_size = DFC_DATA_SIZE_UNUSED;
+		*padding = DFC_PADDING_SIZE_UNUSED;
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+
+/******************************************************************************
+  dfc_send_cmd
+
+  Description:
+	This function configures DFC to send command through DFC to NAND flash
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+	cmd
+	  Specifies type of command to be sent to NAND flash .The LSB of this
+	  parameter defines the first command code for 2-cycles command. The
+	  MSB defines the second command code for 2-cycles command. If MSB is
+	  set to zero, this indicates that one cycle command
+	addr
+	  Address sent out to the flash device withthis command. For page read/
+	  program commands , 4-cycles address is sent. For erase command only
+	  3-cycles address is sent. If it is equal to 0xFFFFFFFF, the address
+	  should not be used.
+	num_pages
+	  It specifies the number of pages of data to be transferred for
+	  a program or read commands. Unused for any other commands than
+	  read/program.
+
+  Output Parameters:
+	None
+  Returns:
+	0
+	  If size setting is returned successfully
+	-EINVAL
+	  If specified command index is not read1/program/erase/reset/readID/
+	  readStatus.
+*******************************************************************************/
+int dfc_send_cmd(struct dfc_context *context, uint16_t cmd,
+			uint32_t addr, int num_pages)
+{
+	struct dfc_flash_info *flash_info = context->flash_info;
+	struct dfc_mode *dfc_mode = context->dfc_mode;
+	uint8_t  cmd2;
+	uint32_t event_out;
+	uint32_t ndcb0=0, ndcb1=0, ndcb2=0, ndcr;
+	int status;
+
+	/* It is a must to set ND_RUN firstly, then write command buffer
+	 * If conversely,it does not work
+	 */
+	dfc_write(context, DFC_NDSR, NDSR_MASK);
+
+	/* Set ND_RUN */
+	ndcr = dfc_read(context, DFC_NDCR);
+	dfc_write(context, DFC_NDCR, (ndcr | NDCR_ND_RUN));
+
+	// Wait for write command request
+	status = dfc_wait_event(context, NDSR_WRCMDREQ,
+		&event_out, NAND_CMD_TIMEOUT, 0);
+
+	if (status) /* Timeout */
+		return status;
+
+	cmd2 = (cmd>>8) & 0xFF;
+	ndcb0 = cmd | (dfc_mode->chip_select<<24) | ((cmd2?1:0)<<19);
+
+	if (cmd == flash_info->read1) {
+		if (0xFFFFFFFF != addr) {
+			ndcb0 |= NDCB0_ADDR_CYC(4);
+			status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+			if (status)
+				return status;
+			ndcb2 = (num_pages - 1) << 8;
+		}
+	} else if (cmd == flash_info->program) {
+		ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
+		ndcb0 |= NDCB0_ADDR_CYC(4);
+		status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+		if (status)
+			return status;
+		ndcb2 = (num_pages-1) << 8;
+	} else if (cmd == flash_info->erase) {
+		ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS;
+		ndcb0 |= NDCB0_ADDR_CYC(3);
+		status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+		if (status)
+			return status;
+	} else if (cmd == flash_info->read_id) {
+		ndcb0 |= NDCB0_CMD_TYPE(3);
+	} else if(cmd == flash_info->read_status) {
+		ndcb0 |= NDCB0_CMD_TYPE(4);
+	} else if(cmd == flash_info->reset) {
+		ndcb0 |= NDCB0_CMD_TYPE(5);
+	} else if (cmd == flash_info->lock) {
+		ndcb0 |= NDCB0_CMD_TYPE(5);
+	} else
+		return -EINVAL;
+
+	/* Write to DFC command register */
+	dfc_write(context, DFC_NDCB0, ndcb0);
+	dfc_write(context, DFC_NDCB0, ndcb1);
+	dfc_write(context, DFC_NDCB0, ndcb2);
+
+	return 0;
+}
+
+/******************************************************************************
+  dfc_stop
+
+  Description:
+	This function clears ND_RUN bit of NDCR.
+  Input Parameters:
+	context--Pointer to DFC context structure
+  Output Parameters:
+	None
+  Returns:
+	None
+*******************************************************************************/
+void dfc_stop(struct dfc_context *context)
+{
+	unsigned int ndcr;
+	ndcr = dfc_read(context, DFC_NDCR);
+	dfc_write(context, DFC_NDCR, (ndcr & ~NDCR_ND_RUN));
+	ndcr = dfc_read(context, DFC_NDCR);
+
+	return;
+}
+
+int dfc_setup_cmd_dma(struct dfc_context *context,
+		uint16_t cmd, uint32_t addr, int num_pages,
+		uint32_t *buf, uint32_t buf_phys,
+		uint32_t next_desc_phys, uint32_t dma_int_en,
+		struct pxa_dma_desc *dma_desc)
+{
+	struct dfc_flash_info *flash_info = context->flash_info;
+	struct dfc_mode *dfc_mode = context->dfc_mode;
+	uint8_t  cmd2;
+	uint32_t event_out;
+	uint32_t ndcb0=0, ndcb1=0, ndcb2=0, ndcr;
+	int status;
+
+	/*
+	 * It is a must to set ND_RUN firstly, then write command buffer
+	 * If conversely,it does not work
+	 */
+	dfc_write(context, DFC_NDSR, NDSR_MASK);
+
+	/* Set ND_RUN */
+	ndcr = dfc_read(context, DFC_NDCR);
+	ndcr |= NDCR_ND_RUN;
+	dfc_write(context, DFC_NDCR, ndcr);
+
+	/* Wait for write command request */
+	status = dfc_wait_event(context, NDSR_WRCMDREQ,
+		&event_out, NAND_CMD_TIMEOUT, 0);
+
+	if (status)
+		return status; /* Timeout */
+
+	cmd2 = (cmd>>8) & 0xFF;
+	ndcb0 = cmd | (dfc_mode->chip_select<<24) | ((cmd2?1:0)<<19);
+
+	if (cmd == flash_info->read1) {
+		if (0xFFFFFFFF != addr) {
+			ndcb0 |= NDCB0_ADDR_CYC(4);
+			status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+			if (status)
+				return status;
+			ndcb2 = (num_pages-1) << 8;
+		}
+	} else if (cmd == flash_info->program) {
+		ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
+		ndcb0 |= NDCB0_ADDR_CYC(4);
+
+		status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+		if (status)
+			return status;
+		ndcb2 = (num_pages-1) << 8;
+	} else if (cmd == flash_info->erase) {
+		ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS;
+		ndcb0 |= NDCB0_ADDR_CYC(3);
+
+		status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
+		if (status)
+			return status;
+	} else if (cmd == flash_info->read_id) {
+		ndcb0 |= NDCB0_CMD_TYPE(3);
+	} else if (cmd == flash_info->read_status) {
+		ndcb0 |= NDCB0_CMD_TYPE(4);
+	} else if (cmd == flash_info->reset) {
+		ndcb0 |= NDCB0_CMD_TYPE(5);
+	} else if (cmd == flash_info->lock) {
+		ndcb0 |= NDCB0_CMD_TYPE(5);
+	} else
+		return -EINVAL;
+
+	*((uint32_t *)buf) = ndcb0;
+	*((uint32_t *)buf + 1) = ndcb1;
+	*((uint32_t *)buf + 2) = ndcb2;
+
+	dma_int_en &= (DCMD_STARTIRQEN | DCMD_ENDIRQEN);
+
+	dma_desc->ddadr = next_desc_phys;
+	dma_desc->dsadr = buf_phys;
+	dma_desc->dtadr = NDCB0_DMA_ADDR;
+	dma_desc->dcmd  = DCMD_INCSRCADDR | DCMD_FLOWTRG | dma_int_en |
+			  DCMD_WIDTH4 | DCMD_BURST16 | 12;
+	return 0;
+}
+
+int dfc_setup_data_dma(struct dfc_context* context,
+		uint16_t cmd, uint32_t buf_phys,
+		uint32_t next_desc_phys, uint32_t dma_int_en,
+		struct pxa_dma_desc* dma_desc)
+{
+	struct dfc_flash_info * flash_info = context->flash_info;
+	int data_size, padding;
+
+	dfc_get_pattern(context, cmd, &data_size, &padding);
+
+	dma_desc->ddadr = next_desc_phys;
+	dma_int_en &= (DCMD_STARTIRQEN | DCMD_ENDIRQEN);
+
+	if (cmd == flash_info->program) {
+
+		dma_desc->dsadr = buf_phys;
+		dma_desc->dtadr = NDDB_DMA_ADDR;
+		dma_desc->dcmd  = DCMD_INCSRCADDR | DCMD_FLOWTRG | dma_int_en |
+				  DCMD_WIDTH4 | DCMD_BURST32 | data_size;
+
+	} else if (cmd == flash_info->read1 || cmd == flash_info->read_id ||
+		   cmd == flash_info->read_status) {
+
+		dma_desc->dsadr = NDDB_DMA_ADDR;
+		dma_desc->dtadr = buf_phys;
+		dma_desc->dcmd  = DCMD_INCTRGADDR | DCMD_FLOWSRC | dma_int_en |
+				  DCMD_WIDTH4 | DCMD_BURST32 | data_size;
+	}
+	else
+		return -EINVAL;
+	return 0;
+}
+
+void dfc_start_cmd_dma(struct dfc_context* context, struct pxa_dma_desc* dma_desc)
+{
+	DRCMR99 = DRCMR_MAPVLD | context->cmd_dma_ch;	/* NAND CMD DRCMR */
+	DDADR(context->cmd_dma_ch) = (uint32_t)dma_desc;
+	DCSR(context->cmd_dma_ch) |= DCSR_RUN;
+}
+
+void dfc_start_data_dma(struct dfc_context* context, struct pxa_dma_desc* dma_desc)
+{
+	DRCMR97 = DRCMR_MAPVLD | context->data_dma_ch;
+	DDADR(context->data_dma_ch) = (uint32_t)dma_desc;
+	DCSR(context->data_dma_ch) |= DCSR_RUN;
+}
+
+/******************************************************************************
+  dfc_read_fifo_partial
+
+  Description:
+	This function reads data from data buffer of DFC.Bytes can be any less than
+	or equal to data_size, the left is ignored by ReadFIFO though they will be
+	read from NDDB to clear data buffer.
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+	nbytes
+	  Indicating how much data should be read into buffer.
+	data_size
+	  Specifing length of data transferred to/from DFC, which includes
+	  padding bytes
+  Output Parameters:
+	pBuffer
+	  Pointer to the data buffer where data should be placed.
+	Returns:
+	  None
+*******************************************************************************/
+void dfc_read_fifo_partial(struct dfc_context *context, uint8_t *buffer,
+		int nbytes, int data_size)
+{
+	uint32_t data = 0;
+	uint32_t i = 0;
+	uint32_t bytes_multi;
+	uint32_t bytes_remain;
+
+
+	if (1 == data_size) {
+		data = dfc_read(context, DFC_NDDB) & 0xFF;
+		*buffer++ = (uint8_t)data;
+	} else if (2 == data_size) {
+		data = dfc_read(context, DFC_NDDB) & 0xFFFF;
+		*buffer++ = data & 0xFF;
+		*buffer++ = (data >> 8) & 0xFF;
+	} else {
+		bytes_multi = (nbytes & 0xFFFFFFFC);
+		bytes_remain = nbytes & 0x03;
+
+		i = 0;
+		/* Read the bytes_multi*4 bytes data */
+		while (i < bytes_multi) {
+			data = dfc_read(context, DFC_NDDB);
+			/* FIXME: we don't know whether the buffer
+			 * align to 4 bytes or not. Cast the buffer
+			 * to int is not safe here. Especially under
+			 * gcc 4.x. Used memcpy here. But the memcpy
+			 * may be not correct on BE architecture.
+			 * --by Yin, Fengwei
+			 */
+			memcpy(buffer, &data, sizeof(data));
+			i += sizeof(data);
+			buffer += sizeof(data);
+		}
+
+		/* Read the left bytes_remain bytes data */
+		if (bytes_remain) {
+			data = dfc_read(context, DFC_NDDB);
+			for (i = 0; i < bytes_remain; i++)
+				*buffer++ = (uint8_t)((data >> (8*i)) & 0xFF);
+		}
+
+		/* When read the remain bytes, we always read 4 bytes data
+		 * to DFC. So the data_size should subtract following number.
+		 */
+		data_size -= bytes_multi + (bytes_remain ? sizeof(data) : 0);
+
+		/* We need Read data_size bytes data totally */
+		while (data_size > 0) {
+			data = dfc_read(context, DFC_NDDB);
+			data_size -= sizeof(data);
+		}
+
+/*
+		while(i < ((uint32_t)data_size) ) {
+			if (i < bytes_multi) {
+				temp = (uint32_t *)buffer;
+				*temp = dfc_reg->nddb;
+			} else if (i == bytes_multi && bytes_remain){
+				uint32_t j = 0;
+				data = dfc_reg->nddb;
+				while (j++ < bytes_remain) {
+					*buffer++ = (uint8_t)	\
+						((data>>(8*j)) & 0xFF);
+				}
+			} else {
+				data = dfc_reg->nddb;
+			}
+			i += 4;
+			buffer += 4;
+		}
+*/
+	}
+	return;
+}
+
+/******************************************************************************
+  dfc_write_fifo_partial
+
+  Description:
+	Write to data buffer of DFC from a buffer. Bytes can be same as
+	data_size, also can be data_size-padding, but can��t be random value,
+	the left will be automatically padded by WriteFIFO.
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+	bytes
+	  Indicating how much data should be read into buffer.
+	data_size
+	  Specifing length of data transferred to/from DFC, which includes
+	  padding bytes
+	buffer
+	  Pointer to the data buffer where data will be taken from to be written
+	  to DFC data buffer
+  Output Parameters:
+	None
+  Returns:
+	None
+*******************************************************************************/
+void dfc_write_fifo_partial(struct dfc_context *context, uint8_t *buffer,
+		int nbytes, int data_size)
+{
+	uint32_t i = 0;
+
+	uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
+	uint32_t bytes_remain = nbytes & 0x03;
+	uint32_t temp;
+	/*
+	 * caller guarantee buffer contains appropriate data thereby
+	 * it is impossible for nbytes not to be a multiple of 4 byte
+	 */
+
+	/* Write the bytes_multi*4 bytes data */
+	while (i < bytes_multi) {
+		temp = buffer[0] | buffer[1] << 8 |
+				buffer[2] << 16 | buffer[3] << 24;
+		dfc_write(context, DFC_NDDB, temp);
+		buffer += 4;
+		i += 4;
+	}
+
+	/* Write the left bytes_remain bytes data */
+	if (bytes_remain) {
+		temp = 0xFFFFFFFF;
+		for (i = 0; i < bytes_remain; i++)
+			temp &= *buffer++ << i*8;
+
+		dfc_write(context, DFC_NDDB, temp);
+	}
+
+	/* When write the remain bytes, we always write 4 bytes data
+	 * to DFC. So the data_size should subtract following number.
+	 */
+	data_size -= bytes_multi + (bytes_remain ? sizeof(temp) : 0);
+
+	while (data_size > 0) {
+		dfc_write(context, DFC_NDDB, 0xFFFFFFFF);
+		data_size -= 4;
+	}
+
+/*
+	while (i < ((uint32_t)data_size)) {
+		if (i < bytes_multi) {
+			temp = (uint32_t *)buffer;
+			dfc_reg->nddb = *temp;
+		}
+		else if (i == bytes_multi && bytes_remain) {
+				uint32_t j = 0, data = 0xFFFFFFFF;
+				while (j < bytes_remain) {
+				  data &= (uint8_t)(*buffer) << j;
+				  buffer++;
+				  j++;
+				}
+			dfc_reg->nddb = data;
+		}
+		else {
+			dfc_reg->nddb = 0xFFFFFFFF;
+		}
+		i += 4;
+		buffer += 4;
+	}
+*/
+
+	return;
+}
+
+/******************************************************************************
+  dfc_read_fifo
+  Description:
+	This function reads data from data buffer of DFC.Bytes can be any less
+	than or equal to data_size, the left is ignored by ReadFIFO though they
+	will be read from NDDB to clear data buffer.
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+	nbytes
+	  Indicating how much data should be read into buffer.
+	data_size
+	  Specifing length of data transferred to/from DFC, which includes
+	  padding bytes
+  Output Parameters:
+	buffer
+	  Pointer to the data buffer where data should be placed.
+  Returns:
+	None
+*******************************************************************************/
+
+void dfc_read_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes)
+{
+	uint32_t i = 0;
+
+	uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
+	uint32_t bytes_remain = nbytes & 0x03;
+	uint32_t temp;
+
+	/* Read the bytes_multi*4 bytes data */
+	while (i < bytes_multi) {
+		temp = dfc_read(context, DFC_NDDB);
+		/* FIXME: we don't know whether the buffer
+		 * align to 4 bytes or not. Cast the buffer
+		 * to int is not safe here. Especially under
+		 * gcc 4.x. Used memcpy here. But the memcpy
+		 * may be not correct on BE architecture.
+		 * --by Yin, Fengwei
+		 */
+		memcpy(buffer, &temp, sizeof(temp));
+		i += sizeof(temp);
+		buffer += sizeof(temp);
+	}
+
+	/* Read the left bytes_remain bytes data */
+	temp = dfc_read(context, DFC_NDDB);
+	for (i = 0; i < bytes_remain; i++) {
+		*buffer++ = (uint8_t)((temp >> (8*i)) & 0xFF);
+	}
+
+/*
+	while (i < bytes_multi) {
+	    temp = (uint32_t *)buffer;
+	    *temp = dfc_reg->nddb;
+	    i += 4;
+	    buffer += 4;
+	}
+
+	if (bytes_remain) {
+		data = dfc_reg->nddb;
+		for (i = 0; i < bytes_remain; i++) {
+			*buffer++ = (uint8_t)((data>>(8*i)) & 0xFF);
+		}
+	}
+*/
+
+	return;
+}
+
+/******************************************************************************
+  dfc_write_fifo
+  Description:
+	Write to data buffer of DFC from a buffer.Bytes can be same as data_size,
+	also can be data_size-padding, but can��t be random value, the left will
+	be automatically padded by WriteFIFO.
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+	nbytes
+	  Indicating how much data should be read into buffer.
+	data_size
+	  Specifing length of data transferred to/from DFC, which includes
+	  padding bytes
+	buffer
+	  Pointer to the data buffer where data will be taken from to be written to
+	  DFC data buffer
+  Output Parameters:
+	None
+  Returns:
+	None
+*******************************************************************************/
+void dfc_write_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes)
+{
+	uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
+	uint32_t bytes_remain = nbytes & 0x03;
+	uint32_t i=0;
+	uint32_t temp;
+
+	/* Write the bytes_multi*4 bytes data */
+	while (i < bytes_multi) {
+		temp = buffer[0] | buffer[1] << 8 |
+				buffer[2] << 16 | buffer[3] << 24;
+		dfc_write(context, DFC_NDDB, temp);
+		buffer += 4;
+		i += 4;
+	}
+
+	/* Write the left bytes_remain bytes data */
+	temp = 0xFFFFFFFF;
+	for (i = 0; i < bytes_remain; i++)
+		temp &= *buffer++ << i*8;
+	dfc_write(context, DFC_NDDB, temp);
+
+/*
+	while (i < nbytes) {
+	    temp = (uint32_t *)buffer;
+	    dfc_reg->nddb = *temp;
+	    i += 4;
+	    buffer += 4;
+	}
+*/
+}
+
+/******************************************************************************
+  dfc_read_badblock_addr
+
+  Description:
+	This function reads bad block address in units of block starting from 0
+	if bad block is detected. It takes into the account if the operation is
+	for CS0 or CS1  depending on settings of chip_select parameter of DFC
+	Mode structure.
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+  Output Parameters:
+	pBadBlockAddr
+	  Used to retrieve bad block address back to caller if bad block is
+	  detected
+  Returns:
+	None
+*******************************************************************************/
+void dfc_read_badblock_addr(struct dfc_context *context, uint32_t *bbaddr)
+{
+	uint32_t ndbdr;
+	if (0 == context->dfc_mode->chip_select)
+		ndbdr = dfc_read(context, DFC_NDBDR0);
+	else
+		ndbdr = dfc_read(context, DFC_NDBDR1);
+
+	if (512 == context->flash_info->page_size) {
+		ndbdr = (ndbdr >> 5) & 0xFFF;
+		*bbaddr = ndbdr;
+	} else if (2048 == context->flash_info->page_size) {
+		/* 16 bits LB */
+		ndbdr = (ndbdr >> 8);
+		*bbaddr = ndbdr;
+	}
+	return;
+}
+
+/******************************************************************************
+  dfc_enable_int
+
+  Description:
+	This function is used to enable DFC interrupts.	The bits in int_mask
+	will be used to unmask NDCR register to enable corresponding interrupts.
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+	int_mask
+	  Specifies what interrupts to enable
+  Output Parameters:
+	None
+  Returns:
+	None
+*******************************************************************************/
+void dfc_enable_int(struct dfc_context *context, uint32_t int_mask)
+{
+	uint32_t ndcr;
+
+	ndcr = dfc_read(context, DFC_NDCR);
+	ndcr &= ~int_mask;
+	dfc_write(context, DFC_NDCR, ndcr);
+
+	ndcr = dfc_read(context, DFC_NDCR);
+	return;
+}
+
+/******************************************************************************
+  dfc_disable_int
+
+  Description:
+	This function is used to disable DFC interrupts.
+	The bits inint_mask will be used to mask NDCR register to disable
+	corresponding interrupts.
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+	int_mask
+	  Specifies what interrupts to disable
+  Output Parameters:
+	None
+  Returns:
+	None
+*******************************************************************************/
+void dfc_disable_int(struct dfc_context *context, uint32_t int_mask)
+{
+	uint32_t ndcr;
+
+	ndcr = dfc_read(context, DFC_NDCR);
+	ndcr |= int_mask;
+	dfc_write(context, DFC_NDCR, ndcr);
+
+	ndcr = dfc_read(context, DFC_NDCR);
+	return;
+}
+
+/******************************************************************************
+  dfc_clear_int
+
+  Description:
+	This function is used to disable DFC interrupts.
+	The bits in int_mask will be used to clear corresponding interrupts
+	in NDCR register
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+	int_mask
+	  Specifies what interrupts to clear
+  Output Parameters:
+	None
+  Returns:
+	None
+*******************************************************************************/
+void dfc_clear_int(struct dfc_context *context, uint32_t int_mask)
+{
+	dfc_write(context, DFC_NDSR, int_mask);
+
+	dfc_read(context, DFC_NDSR);
+	return;
+}
+
+/*
+ * high level primitives
+ */
+
+/******************************************************************************
+  dfc_init
+
+  Description:
+	This function does entire DFC initialization according to the NAND
+	flash type currently used with platform, including setting MFP, set
+	flash timing, set DFC mode, configuring specified flash parameters
+	in DFC, clear ECC logic and page count register.
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+  Output Parameters:
+	None
+  Returns:
+	0
+	  if MFPRs are set correctly
+	-EINVAL
+	  if specified flash is not support by check bytes per page and pages per
+	  block
+******************************************************************************/
+
+static mfp_cfg_t pxa300_nand_cfg[] = {
+	/* NAND */
+	MFP_CFG_X(DF_INT_RnB, AF0, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nRE_nOE, AF1, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nWE, AF1, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_CLE_nOE, AF0, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nADV1_ALE, AF1, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nCS0, AF1, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nCS1, AF0, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_IO0, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO1, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO2, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO3, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO4, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO5, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO6, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO7, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO8, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO9, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO10, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO11, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO12, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO13, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO14, AF1, DS08X, PULL_LOW),
+};
+
+#define ARRAY_AND_SIZE(x)	(x), ARRAY_SIZE(x)
+
+int dfc_init(struct dfc_context* context, int type)
+{
+	int status;
+	struct dfc_flash_info * flash_info;
+	uint32_t ndcr = 0x00000FFF; /* disable all interrupts */
+
+	status = dfc_get_flash_info(type, &flash_info);
+	if (status)
+		return status;
+	context->flash_info = flash_info;
+
+	pxa3xx_mfp_config(ARRAY_AND_SIZE(pxa300_nand_cfg));
+	//enable_dfc_pins();
+
+	dfc_set_timing(context, &context->flash_info->timing);
+
+	if (flash_info->enable_arbiter)
+		ndcr |= NDCR_ND_ARB_EN;
+
+	if (64 == flash_info->page_per_block)
+		ndcr |= NDCR_PG_PER_BLK;
+	else if (32 != flash_info->page_per_block)
+		return -EINVAL;
+
+	if (flash_info->row_addr_start)
+		ndcr |= NDCR_RA_START;
+
+	ndcr |=  (flash_info->read_id_bytes)<<16;
+
+	ndcr |= (flash_info->dfc_mode) << 21;
+
+	if (flash_info->ncsx)
+		ndcr |= NDCR_NCSX;
+
+	if (2048 == flash_info->page_size)
+		ndcr |= NDCR_PAGE_SZ;
+	else if (512 != flash_info->page_size)
+		return -EINVAL;
+
+	if (16 == flash_info->flash_width)
+		ndcr |= NDCR_DWIDTH_M;
+	else if (8 != flash_info->flash_width)
+		return -EINVAL;
+
+	if (16 == flash_info->dfc_width)
+		ndcr |= NDCR_DWIDTH_C;
+	else if (8 != flash_info->dfc_width)
+		return -EINVAL;
+
+	dfc_write(context, DFC_NDCR, ndcr);
+
+	dfc_set_dma(context);
+	dfc_set_ecc(context);
+	dfc_set_spare(context);
+
+	return 0;
+}
+
+/******************************************************************************
+  dfc_init_no_gpio
+
+  Description:
+	This function does entire DFC initialization according to the NAND
+	flash type currently used with platform, including set flash timing,
+	set DFC mode, configuring specified flash parameters in DFC, clear
+	ECC logic and page count register. The only difference with dfc_init
+	is that it does not set MFP&GPIO, very useful in OS loader
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+  Output Parameters:
+	None
+  Returns:
+	0
+	  if MFPRs are set correctly
+	-EINVAL
+	  if specified flash is not support by check bytes per page and pages
+	  per block
+******************************************************************************/
+int dfc_init_no_gpio(struct dfc_context* context, int type)
+{
+	struct dfc_flash_info * flash_info;
+	uint32_t ndcr = 0x00000FFF; /* disable all interrupts */
+	int status;
+
+	status = dfc_get_flash_info(type, &flash_info);
+	if (status)
+		return status;
+	context->flash_info = flash_info;
+
+	dfc_set_timing(context, &context->flash_info->timing);
+
+	if (flash_info->enable_arbiter)
+		ndcr |= NDCR_ND_ARB_EN;
+
+	if (64 == flash_info->page_per_block)
+		ndcr |= NDCR_PG_PER_BLK;
+	else if (32 != flash_info->page_per_block)
+		return -EINVAL;
+
+	if (flash_info->row_addr_start)
+		ndcr |= NDCR_RA_START;
+
+	ndcr |=  (flash_info->read_id_bytes)<<16;
+
+	ndcr |= (flash_info->dfc_mode) << 21;
+
+	if (flash_info->ncsx)
+		ndcr |= NDCR_NCSX;
+
+	if (2048 == flash_info->page_size)
+		ndcr |= NDCR_PAGE_SZ;
+	else if (512 != flash_info->page_size)
+		return -EINVAL;
+
+	if (16 == flash_info->flash_width)
+		ndcr |= NDCR_DWIDTH_M;
+	else if (8 != flash_info->flash_width)
+		return -EINVAL;
+
+	if (16 == flash_info->dfc_width)
+		ndcr |= NDCR_DWIDTH_C;
+	else if (8 != flash_info->dfc_width)
+		return -EINVAL;
+
+	dfc_write(context, DFC_NDCR, ndcr);
+
+	dfc_set_dma(context);
+	dfc_set_ecc(context);
+	dfc_set_spare(context);
+
+	return 0;
+}
+
+/*
+ * This macro will be used in following NAND operation functions.
+ * It is used to clear command buffer to ensure cmd buffer is empty
+ * in case of operation is timeout
+ */
+#define ClearCMDBuf() 	do {					\
+				dfc_stop(context); 		\
+				udelay(NAND_OTHER_TIMEOUT);	\
+			} while (0)
+
+/******************************************************************************
+  dfc_reset_flash
+
+  Description:
+	It reset the flash. The function can be called at any time when the
+	device is in Busy state during random read/program/erase mode and
+	reset operation will abort all these operations. After reset operation
+	the device is ready to wait for next command
+  Input Parameters:
+	context
+	  Pointer to DFC context structure
+  Output Parameters:
+	None
+  Returns:
+	0
+	  execution succeeds
+	-ETIME
+	  if timeout
+*******************************************************************************/
+int dfc_reset_flash(struct dfc_context *context)
+{
+	struct dfc_flash_info *flash_info = context->flash_info;
+	uint32_t event, event_out;
+	unsigned long timeo;
+	int status;
+
+	/* Send command */
+	dfc_send_cmd(context, (uint16_t)flash_info->reset, 0xFFFFFFFF, 0);
+
+	event = (context->dfc_mode->chip_select)? \
+			NDSR_CS1_CMDD : NDSR_CS0_CMDD;
+
+	/* Wait for CMDDM(command done successfully) */
+	status = dfc_wait_event(context, event, &event_out,
+		NAND_OTHER_TIMEOUT, 0);
+
+	if (status) {
+		ClearCMDBuf();
+		return status;
+	}
+
+
+	/* Wait until flash device is stable or timeout (10ms) */
+	timeo = jiffies + HZ;
+	do {
+		if (monahans_df_dev_ready(context->mtd))
+			break;
+	} while (time_before(jiffies, timeo));
+
+	return 0;
+}
+
+int dfc_readid(struct dfc_context *context, uint32_t *id)
+{
+	struct dfc_flash_info *flash_info = context->flash_info;
+	uint32_t event_out;
+	int status;
+	char tmp[DFC_DATA_SIZE_ID];
+
+	/* Send command */
+	status = dfc_send_cmd(context, (uint16_t)flash_info->read_id,
+			0xFFFFFFFF, 0);
+	if (status) {
+		ClearCMDBuf();
+		return status;
+	}
+
+	/* Wait for CMDDM(command done successfully) */
+	status = dfc_wait_event(context, NDSR_RDDREQ, &event_out,
+		NAND_OTHER_TIMEOUT, 0);
+	if (status) {
+		ClearCMDBuf();
+		return status;
+	}
+	dfc_read_fifo_partial(context, (unsigned char *)tmp,
+			context->flash_info->read_id_bytes, DFC_DATA_SIZE_ID);
+
+	*id = tmp[0] | (tmp[1] << 8);
+	return 0;
+}
+
+#define ERR_NONE		0x0
+#define ERR_DMABUSERR		(-0x01)
+#define ERR_SENDCMD		(-0x02)
+#define ERR_DBERR		(-0x03)
+#define ERR_BBERR		(-0x04)
+#define ERR_BUSY		(-0x05)
+
+#define STATE_CMD_SEND		0x1
+#define STATE_CMD_HANDLE	0x2
+#define STATE_DMA_TRANSFER	0x3
+#define STATE_DMA_DONE		0x4
+#define STATE_READY		0x5
+#define STATE_SUSPENDED		0x6
+#define	STATE_DATA_TRANSFER	0x7
+
+#define NAND_RELOC_MAX		127
+#define NAND_RELOC_HEADER	0x524e
+#define MAX_CHIP		1
+#define NAND_CMD_DMA_LEN	12
+
+#define MAX_TIM_SIZE	0x1000
+#define MAX_BBT_SLOTS	24
+
+struct reloc_item {
+	unsigned short from;
+	unsigned short to;
+};
+
+struct reloc_table {
+	unsigned short header;
+	unsigned short total;
+	struct reloc_item reloc[NAND_RELOC_MAX];
+};
+
+struct monahans_dfc_info {
+	unsigned int		state;
+	struct dfc_context	*context;
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+	dma_addr_t 		data_buf_addr;
+	char			*data_buf;
+	int 			data_dma;
+	struct pxa_dma_desc	*data_desc;
+	dma_addr_t 		data_desc_addr;
+	dma_addr_t 		cmd_buf_addr;
+	char 			*cmd_buf;
+	int 			cmd_dma;
+	struct pxa_dma_desc	*cmd_desc;
+	dma_addr_t 		cmd_desc_addr;
+	u64 			dma_mask;
+#else
+	char 			*data_buf;
+#endif
+	u32 			current_slot;
+	struct reloc_table 	table;
+	unsigned int 		table_init;
+	/* relate to the command */
+	unsigned int 		cmd;
+	unsigned int 		addr;
+	unsigned int 		column;
+	int 			retcode;
+	unsigned int 		buf_count;
+	struct completion 	cmd_complete;
+};
+
+static struct dfc_mode dfc_mode =
+{
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+	1,	/* enable DMA */
+#else
+	0,
+#endif
+	1,	/* enable ECC */
+	1,	/* enable SPARE */
+	0,	/* CS0 */
+};
+
+
+struct dfc_context dfc_context =
+{
+	0,	/* Initialized at function monahans_df_init() */
+	&dfc_mode,
+	0,	/* data dma channel */
+	0,	/* cmd dma channel */
+	NULL, 	/* &zylonite_flashinfo */
+};
+
+
+/*
+ * MTD structure for Zylonite board
+ */
+static struct mtd_info *monahans_mtd = NULL;
+
+/*
+ * BootRom and XDB will use last 127 block, and they will keep all the status
+ * of the bootloader and image, so skip the first 2M size and last 2M size
+ */
+static struct mtd_partition partition_info[] = {
+	{
+		name:		"Bootloader",
+//#ifdef	CONFIG_CPU_MONAHANS_LV
+		size:		0x00060000,
+//#else
+//		size:		0x00040000,
+//#endif
+		offset:		0,
+		mask_flags:	MTD_WRITEABLE  /* force read-only */
+	},{
+		name:		"Kernel",
+		size:		0x00200000,
+//#ifdef	CONFIG_CPU_MONAHANS_LV
+		offset:		0x00060000,
+//#else
+//		offset:		0x00040000,
+//#endif
+		mask_flags:	MTD_WRITEABLE  /* force read-only */
+	},{
+		name:		"Filesystem",
+		size:		0x05000000,
+//#ifdef	CONFIG_CPU_MONAHANS_LV
+		offset:		0x00260000,
+//#else
+//		offset:		0x00240000,
+//#endif
+	}, {
+		name:		"MassStorage",
+		size:		0x0, /* It will be set at probe function */
+		offset:		MTDPART_OFS_APPEND /* Append after fs section */
+	}, {
+		name:		"BBT",
+		size:		0x0, /* It will be set at probe function */
+		offset:		MTDPART_OFS_APPEND,/* Append after fs section */
+		mask_flags:	MTD_WRITEABLE  /* force read-only */
+	}
+};
+
+#define		PART_NUM	ARRAY_SIZE(partition_info)
+
+/* MHN_OBM_V2 is related to BBT in MOBM V2
+ * MHN_OBM_V3 is related to BBT in MOBM V3
+ */
+enum {
+	MHN_OBM_NULL = 0,
+	MHN_OBM_V1,
+	MHN_OBM_V2,
+	MHN_OBM_V3,
+	MHN_OBM_INVAL
+} MHN_OBM_TYPE;
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+static uint8_t scan_main_bbt_pattern[] = { 'p', 'x', 'a', '1' };
+static uint8_t scan_mirror_bbt_pattern[] = { '0', 'a', 'x', 'p' };
+
+static struct nand_bbt_descr monahans_bbt_default = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+			| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.maxblocks = 2,
+	.len = 2,
+	.offs = 0,
+	.pattern = scan_ff_pattern,
+};
+
+static struct nand_bbt_descr monahans_bbt_main = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+			| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.veroffs = 6,
+	.maxblocks = 2,
+        .offs = 2,
+        .len = 4,
+        .pattern = scan_main_bbt_pattern,
+};
+
+static struct nand_bbt_descr monahans_bbt_mirror = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+			| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.veroffs = 6,
+	.maxblocks = 2,
+        .offs = 2,
+        .len = 4,
+        .pattern = scan_mirror_bbt_pattern,
+};
+
+#if 0
+static struct nand_bbt_descr monahans_bbt_main = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+			| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.veroffs = 2,
+	.maxblocks = 2,
+        .offs = 		0x0,
+        .len = 			2,
+        .pattern = 		scan_ff_pattern
+};
+static struct nand_bbt_descr monahans_bbt_mirror = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+			| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.veroffs = 2,
+	.maxblocks = 2,
+        .offs = 0x0,
+        .len = 2,
+        .pattern = scan_ff_pattern
+};
+#endif
+
+static struct nand_ecclayout monahans_lb_nand_oob = {
+	.eccbytes = 24,
+	.eccpos = {
+		40, 41, 42, 43, 44, 45, 46, 47,
+		48, 49, 50, 51, 52, 53, 54, 55,
+		56, 57, 58, 59, 60, 61, 62, 63},
+	.oobfree = { {2, 38} }
+};
+
+/*
+ * Monahans OOB size is only 8 bytes, and the rest 8 bytes is controlled by
+ * hardware for ECC. We construct virutal ECC buffer. Acutally, ECC is 6 bytes
+ * and the remain 2 bytes are reserved.
+ */
+static struct nand_ecclayout monahans_sb_nand_oob = {
+	.eccbytes = 6,
+	.eccpos = {8, 9, 10, 11, 12, 13 },
+	.oobfree = { {2, 6} }
+};
+
+
+static inline int is_buf_blank(u8 * buf, int size)
+{
+	int i = 0;
+	while(i < size) {
+		if (*((unsigned long *)(buf + i)) != 0xFFFFFFFF)
+			return 0;
+		i += 4;
+	}
+	if (i > size) {
+		i -= 4;
+		while( i < size) {
+			if(*(buf + i) != 0xFF)
+				return 0;
+			i++;
+		}
+	}
+	return 1;
+}
+
+static void print_buf(char *buf, int num)
+{
+	int i = 0;
+
+	while (i < num) {
+		printk(KERN_ERR "0x%08x: %02x %02x %02x %02x %02x %02x %02x"
+		" %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+		(unsigned int) (i),  buf[i], buf[i+1], buf[i+2],
+		buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7],
+		buf[i+8], buf[i+9], buf[i+10],buf[i+11], buf[i+12],
+		buf[i+13], buf[i+14], buf[i+15]);
+		i += 16;
+	}
+}
+
+static int inline enable_dfc_dma(struct dfc_context *context, int enable)
+{
+	int ret = dfc_mode.enable_dma;
+	unsigned long ndcr;
+
+	if (!enable) {
+		ndcr = dfc_read(context, DFC_NDCR);
+		ndcr &= ~NDCR_DMA_EN;
+		dfc_write(context, DFC_NDCR, ndcr);
+		dfc_mode.enable_dma = 0;
+	} else {
+		ndcr = dfc_read(context, DFC_NDCR);
+		ndcr |= NDCR_DMA_EN;
+		dfc_write(context, DFC_NDCR, ndcr);
+		dfc_mode.enable_dma = 1;
+	}
+	return ret;
+}
+
+
+static void inline dump_info(struct monahans_dfc_info *info)
+{
+	if (!info)
+		return;
+
+	printk(KERN_ERR "cmd:0x%x; addr:0x%x; retcode:%d; state:%d \n",
+		info->cmd, info->addr, info->retcode, info->state);
+}
+
+static void inline  enable_hw_ecc(struct dfc_context* context, int enable)
+{
+	unsigned long ndcr;
+
+	if (!enable) {
+		ndcr = dfc_read(context, DFC_NDCR);
+		ndcr &= ~NDCR_ECC_EN;
+		dfc_write(context, DFC_NDCR, ndcr);
+		dfc_mode.enable_ecc = 0;
+	}
+	else {
+		ndcr = dfc_read(context, DFC_NDCR);
+		ndcr |= NDCR_ECC_EN;
+		dfc_write(context, DFC_NDCR, ndcr);
+		dfc_mode.enable_ecc = 1;
+	}
+}
+
+/*
+ * Now, we are not sure that the NDSR_RDY mean the flash is ready.
+ * Need more test.
+ */
+static int monahans_df_dev_ready(struct mtd_info *mtd)
+{
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+				(((struct nand_chip *)(mtd->priv))->priv);
+
+	struct dfc_context* context = info->context;
+
+	return ((dfc_read(context, DFC_NDSR) & NDSR_RDY));
+}
+
+/* each read, we can only read 4bytes from NDDB, we must buffer it */
+static u_char monahans_df_read_byte(struct mtd_info *mtd)
+{
+	char retval = 0xFF;
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+				(((struct nand_chip *)(mtd->priv))->priv);
+
+	if (info->column < info->buf_count) {
+		/* Has just send a new command? */
+		retval = info->data_buf[info->column++];
+	}
+	return retval;
+}
+
+static void monahans_df_write_byte(struct mtd_info *mtd, u8 byte)
+{
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+				(((struct nand_chip *)(mtd->priv))->priv);
+	info->data_buf[info->column++] = byte;
+}
+
+static u16 monahans_df_read_word(struct mtd_info *mtd)
+{
+	u16 retval = 0xFFFF;
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+				(((struct nand_chip *)(mtd->priv))->priv);
+
+	if (!(info->column & 0x01) && info->column < info->buf_count) {
+		retval = *((u16 *)(info->data_buf+info->column));
+		info->column += 2;
+	}
+	return retval;
+}
+
+static void monahans_df_write_word(struct mtd_info *mtd, u16 word)
+{
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+				(((struct nand_chip *)(mtd->priv))->priv);
+
+	if (!(info->column & 0x01) && info->column < info->buf_count) {
+		*((u16 *)(info->data_buf+info->column)) = word;
+		info->column += 2;
+	}
+}
+
+static void monahans_df_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+				(((struct nand_chip *)(mtd->priv))->priv);
+	int real_len = min((unsigned int)len, info->buf_count - info->column);
+
+	memcpy(buf, info->data_buf + info->column, real_len);
+	info->column += real_len;
+}
+
+static void monahans_df_write_buf(struct mtd_info *mtd,
+		const u_char *buf, int len)
+{
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+				(((struct nand_chip *)(mtd->priv))->priv);
+	int real_len = min((unsigned int)len, info->buf_count - info->column);
+
+	memcpy(info->data_buf + info->column, buf, real_len);
+	info->column += real_len;
+}
+
+static int monahans_df_verify_buf(struct mtd_info *mtd,
+		const u_char *buf, int len)
+{
+	return 0;
+}
+
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+static void monahans_dfc_cmd_dma_irq(int channel, void *data,
+		struct pt_regs *regs)
+{
+	unsigned int dcsr;
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)data;
+	struct dfc_context* context = info->context;
+	struct dfc_mode* dfc_mode = context->dfc_mode;
+	unsigned int intm;
+
+	dcsr = DCSR(channel);
+	DCSR(channel) = dcsr;
+
+	intm = (dfc_mode->chip_select) ? \
+		(NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+	D1(printk("cmd dma interrupt, channel:%d, DCSR:0x%08x\n", \
+			channel, dcsr));
+
+	if (dcsr & DCSR_BUSERR) {
+		info->retcode = ERR_DMABUSERR;
+		complete(&info->cmd_complete);
+	} else {
+		if ((info->cmd == NAND_CMD_READ0) ||
+				(info->cmd == NAND_CMD_READOOB)|| \
+				(info->cmd == NAND_CMD_READID) || \
+				(info->cmd == NAND_CMD_STATUS)) {
+			dfc_enable_int(context, NDSR_RDDREQ | NDSR_DBERR);
+		} else if (info->cmd == NAND_CMD_PAGEPROG)
+			dfc_enable_int(context, NDSR_WRDREQ);
+		else if (info->cmd == NAND_CMD_ERASE1)
+			dfc_enable_int(context, intm);
+	}
+
+	return;
+}
+
+
+static void monahans_dfc_data_dma_irq(int channel, void *data,
+		struct pt_regs *regs)
+{
+	unsigned int dcsr, intm;
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)data;
+	struct dfc_context* context = info->context;
+	struct dfc_mode* dfc_mode = context->dfc_mode;
+
+	dcsr = DCSR(channel);
+	DCSR(channel) = dcsr;
+
+	intm = (dfc_mode->chip_select) ? \
+		(NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+	D1(printk("data dma interrupt, channel:%d, DCSR:0x%08x\n",
+			channel, dcsr));
+	if (dcsr & DCSR_BUSERR) {
+		info->retcode = ERR_DMABUSERR;
+		complete(&info->cmd_complete);
+	}
+
+	if (info->cmd == NAND_CMD_PAGEPROG) {
+		/* DMA interrupt may be interrupted by other IRQs*/
+		info->state = STATE_DMA_DONE;
+		dfc_enable_int(context, intm);
+	} else {
+		info->state = STATE_READY;
+		complete(&info->cmd_complete);
+	}
+
+}
+#endif
+
+static irqreturn_t monahans_dfc_irq(int irq, void *devid)
+{
+	unsigned int status, event, intm, cmd;
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)devid;
+	struct dfc_context* context = info->context;
+	struct dfc_mode* dfc_mode = context->dfc_mode;
+
+	intm =  (dfc_mode->chip_select) ? \
+		(NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
+	event = (dfc_mode->chip_select) ? \
+		(NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+	status = dfc_read(context, DFC_NDSR);
+	D1(printk("DFC irq, NDSR:0x%x\n", status));
+	if (status & (NDSR_RDDREQ | NDSR_DBERR)) {
+		if (status & NDSR_DBERR) {
+			info->retcode = ERR_DBERR;
+		}
+
+		dfc_disable_int(context, NDSR_RDDREQ | NDSR_DBERR);
+		dfc_clear_int(context, NDSR_RDDREQ | NDSR_DBERR);
+		if (info->cmd == NAND_CMD_READID)
+			cmd = context->flash_info->read_id;
+		else if (info->cmd == NAND_CMD_STATUS)
+			cmd = context->flash_info->read_status;
+		else if (info->cmd == NAND_CMD_READ0 ||
+				info->cmd == NAND_CMD_READOOB)
+			cmd = context->flash_info->read1;
+		else {
+			printk(KERN_ERR "No according command:0x%x happens\n",
+					info->cmd);
+			goto out;
+		}
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+		info->state = STATE_DMA_TRANSFER;
+		dfc_start_data_dma(context,
+				(struct pxa_dma_desc*)info->data_desc_addr);
+#else
+		info->state = STATE_DATA_TRANSFER;
+		complete(&info->cmd_complete);
+#endif
+	} else if (status & NDSR_WRDREQ) {
+		dfc_disable_int(context, NDSR_WRDREQ);
+		dfc_clear_int(context, NDSR_WRDREQ);
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+		info->state = STATE_DMA_TRANSFER;
+		dfc_start_data_dma(context,
+				(struct pxa_dma_desc*)info->data_desc_addr);
+#else
+		info->state = STATE_DATA_TRANSFER;
+		complete(&info->cmd_complete);
+#endif
+	} else if (status & event) {
+		if (status & NDSR_CS0_BBD) {
+			info->retcode = ERR_BBERR;
+		}
+
+		dfc_disable_int(context, intm);
+		dfc_clear_int(context, event);
+		info->state = STATE_READY;
+		complete(&info->cmd_complete);
+	}
+out:
+	return IRQ_HANDLED;
+}
+
+static int dfc_send_command(struct mtd_info *mtd, unsigned int cmd,
+				unsigned int addr, unsigned int num_pages,
+				unsigned int event)
+{
+
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+			(((struct nand_chip *)(mtd->priv))->priv);
+	struct dfc_context* context = info->context;
+	int status;
+	int ret;
+
+	D1(printk("ready send command, cmd:0x%x, at address:0x%x,"
+		" num_pages:%d, wait event:0x%x\n", cmd, addr, num_pages, event));
+
+	info->state = STATE_CMD_SEND;
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+	status = dfc_setup_cmd_dma(context, cmd, addr, num_pages,
+			(uint32_t *)info->cmd_buf, info->cmd_buf_addr,
+			DDADR_STOP, DCMD_ENDIRQEN, info->cmd_desc);
+#else
+	status = dfc_send_cmd(context, cmd, addr, num_pages);
+#endif
+	if (status) {
+		info->retcode = ERR_SENDCMD;
+		dfc_stop(context);
+		udelay(20);
+		printk(KERN_ERR "fail send command\n");
+		return info->retcode;
+	}
+	info->state = STATE_CMD_HANDLE;
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+	dfc_setup_data_dma(context, cmd, info->data_buf_addr,
+			DDADR_STOP, DCMD_ENDIRQEN, info->data_desc);
+	dfc_start_cmd_dma(context, (struct pxa_dma_desc*)info->cmd_desc_addr);
+#endif
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+	dfc_enable_int(context, event);
+#endif
+	ret = wait_for_completion_timeout(&info->cmd_complete, 2*HZ);
+	if (!ret){
+		printk(KERN_ERR "Command time out\n");
+		dump_info(info);
+	}
+	D1(printk("command return, cmd:0x%x, retcode:%d\n",
+			info->cmd, info->retcode));
+	return 0;
+}
+
+static void monahans_df_command(struct mtd_info *mtd, unsigned command,
+		int column, int page_addr )
+{
+	struct nand_chip *this = (struct nand_chip *)(mtd->priv);
+	struct monahans_dfc_info *info =
+			(struct monahans_dfc_info *)(this->priv);
+	struct dfc_context *context = info->context;
+	struct dfc_flash_info * flash_info = context->flash_info;
+	int ret, pages_shift;
+	int status;
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+	int datasize;
+       	int paddingsize;
+#endif
+	unsigned int to;
+
+	D1(printk("command:0x%x at address:0x%x, column:0x%x\n",
+			command, page_addr, column));
+
+	if (info->state != STATE_READY) {
+		printk(KERN_ERR "CHIP is not ready.\n");
+		dump_info(info);
+		info->retcode = ERR_BUSY;
+		return;
+	}
+	info->retcode = ERR_NONE;
+	pages_shift = this->phys_erase_shift - this->page_shift;
+	if (info->table_init) {
+		to = search_rel_block((page_addr >> pages_shift), mtd);
+	if (to) {
+			page_addr = (to << pages_shift) | (page_addr
+					& ((1 << pages_shift) - 1));
+		}
+	}
+
+	switch ( command ) {
+	case NAND_CMD_READOOB:
+		/*
+		 * DFC has mark the last 8 bytes OOB data if HARDEARE_ECC is
+		 * enabled. We must first disable the HARDWARE_ECC for getting
+		 * all the 16 bytes OOB
+		 */
+		enable_hw_ecc(context, 0);
+		info->buf_count = mtd->writesize + mtd->oobsize;
+		info->column = mtd->writesize + column;
+		info->cmd = command;
+		info->addr = page_addr << this->page_shift;
+		ret = dfc_send_command(mtd, flash_info->read1, info->addr,
+				1, NDSR_RDDREQ | NDSR_DBERR);
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+		dfc_get_pattern(info->context, flash_info->read1, &datasize,
+				&paddingsize);
+		dfc_read_fifo_partial(info->context, info->data_buf,
+				min(info->buf_count, datasize), datasize);
+		info->state = STATE_READY;
+#endif
+		/* We only are OOB, so if the data has error, does not matter */
+		if (info->retcode == ERR_DBERR)
+			info->retcode = ERR_NONE;
+		enable_hw_ecc(context, 1);
+		break;
+
+	case NAND_CMD_READ0:
+		enable_hw_ecc(context, 1);
+		info->column = column;
+		info->cmd = command;
+		info->buf_count = mtd->writesize + mtd->oobsize;
+		memset(info->data_buf, 0xFF, info->buf_count);
+		info->addr = page_addr << this->page_shift;
+
+		ret = dfc_send_command(mtd, flash_info->read1, info->addr,
+				1, NDSR_RDDREQ | NDSR_DBERR);
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+		dfc_get_pattern(info->context, flash_info->read1, &datasize,
+				&paddingsize);
+		dfc_read_fifo_partial(info->context, info->data_buf,
+				min(info->buf_count, datasize), datasize);
+		info->state = STATE_READY;
+#endif
+		/* When the data buf is blank, the DFC will report DB error */
+		if (info->retcode == ERR_DBERR && is_buf_blank(info->data_buf,
+				mtd->writesize))
+			info->retcode = ERR_NONE;
+
+		if (info->retcode == ERR_DBERR) {
+			printk(KERN_ERR "DB error at address 0x%x\n",
+				info->addr);
+			print_buf(info->data_buf, info->buf_count);
+		}
+		break;
+	case NAND_CMD_SEQIN:
+		/* Write only OOB? */
+
+		info->cmd = command;
+		if (column >= mtd->writesize) {
+			info->buf_count = mtd->writesize + mtd->oobsize;
+			enable_hw_ecc(context, 0);
+		} else {
+			info->buf_count = mtd->writesize + mtd->oobsize;
+			enable_hw_ecc(context, 1);
+		}
+		memset(info->data_buf, 0xFF, mtd->writesize + mtd->oobsize);
+		info->column = column;
+		info->addr = page_addr << this->page_shift;
+		break;
+	case NAND_CMD_PAGEPROG:
+		/* prevois command is NAND_CMD_SEIN ?*/
+		if (info->cmd != NAND_CMD_SEQIN) {
+			info->cmd = command;
+			info->retcode = ERR_SENDCMD;
+			printk(KERN_ERR "Monahans NAND device: "
+				"No NAND_CMD_SEQIN executed before.\n");
+			enable_hw_ecc(context, 1);
+			break;
+		}
+		info->cmd = command;
+		ret = dfc_send_command(mtd, flash_info->program, info->addr,
+				1, NDSR_WRDREQ);
+
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+		if (ret != 0)
+			break;
+
+		dfc_get_pattern(info->context, flash_info->program, &datasize,
+				&paddingsize);
+		dfc_write_fifo_partial(info->context, info->data_buf, datasize,
+				datasize);
+
+		if (info->context->dfc_mode->chip_select)
+			dfc_enable_int(info->context,
+				NDSR_CS1_BBD | NDSR_CS1_CMDD);
+		else
+			dfc_enable_int(info->context,
+				NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+		ret = wait_for_completion_timeout(&info->cmd_complete, 2*HZ);
+		if (!ret){
+			printk(KERN_ERR "Programm Command time out\n");
+			dump_info(info);
+		}
+
+		if (info->retcode == ERR_BBERR) {
+			mtd->block_markbad(mtd, info->addr);
+		}
+#endif
+		break;
+	case NAND_CMD_ERASE1:
+		info->cmd = command;
+		info->addr = (page_addr >> pages_shift) << this->phys_erase_shift;
+
+		if (info->context->dfc_mode->chip_select)
+			ret = dfc_send_command(mtd, flash_info->erase,
+				info->addr, 0, NDSR_CS1_BBD | NDSR_CS1_CMDD);
+		else
+			ret = dfc_send_command(mtd, flash_info->erase,
+				info->addr, 0, NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+		if (info->retcode == ERR_BBERR) {
+			mtd->block_markbad(mtd, info->addr);
+		}
+		break;
+	case NAND_CMD_ERASE2:
+		break;
+	case NAND_CMD_READID:
+		info->cmd = command;
+		info->buf_count = flash_info->read_id_bytes;
+		info->column = 0;
+		info->addr = 0xFFFFFFFF;
+		ret = dfc_send_command(mtd, flash_info->read_id, info->addr,
+				0, NDSR_RDDREQ);
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+		dfc_get_pattern(info->context, flash_info->read_id, &datasize,
+				&paddingsize);
+		dfc_read_fifo_partial(info->context, info->data_buf,
+				info->buf_count, datasize);
+		info->state = STATE_READY;
+#endif
+		D1(printk("ReadID, [1]:0x%x, [2]:0x%x\n",
+			info->data_buf[0], info->data_buf[1]));
+		break;
+	case NAND_CMD_STATUS:
+		info->cmd = command;
+		info->buf_count = 1;
+		info->column = 0;
+		info->addr = 0xFFFFFFFF;
+		ret = dfc_send_command(mtd, flash_info->read_status,
+			info->addr, 0, NDSR_RDDREQ);
+#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
+		dfc_get_pattern(info->context, flash_info->read_status,
+			&datasize, &paddingsize);
+		dfc_read_fifo_partial(info->context, info->data_buf,
+			info->buf_count, datasize);
+		info->state = STATE_READY;
+#endif
+		break;
+
+	case NAND_CMD_RESET:
+		status = dfc_reset_flash(&dfc_context);
+		if (status) {
+			printk(KERN_WARNING "Monahans NAND device:"
+				"NAND_CMD_RESET error\n");
+		}
+		break;
+	default:
+		printk(KERN_WARNING "Monahans NAND device:"
+			"Non-support the command.\n");
+		break;
+	}
+
+	if (info->retcode != ERR_NONE)
+		dfc_stop(info->context);
+}
+
+static void monahans_df_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+			(((struct nand_chip *)(mtd->priv))->priv);
+
+	if (chip <= MAX_CHIP)
+		info->context->dfc_mode->chip_select = chip;
+	else
+		printk(KERN_ERR "Monahans NAND device:"
+			"not select the NAND chips!\n");
+}
+
+static int monahans_df_waitfunc(struct mtd_info *mtd,
+		struct nand_chip *this)
+{
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+			(((struct nand_chip *)(mtd->priv))->priv);
+
+	/* monahans_df_send_command has waited for command complete */
+	if (this->state == FL_WRITING || this->state == FL_ERASING) {
+		if (info->retcode == ERR_NONE)
+			return 0;
+		else {
+			/*
+			 * any error make it return 0x01 which will tell
+			 * the caller the erase and write fail
+			 */
+			return 0x01;
+		}
+	}
+
+	return 0;
+}
+
+static int monahans_df_calculate_ecc(struct mtd_info *mtd,
+		const u_char *dat, u_char *ecc_code)
+{
+	return 0;
+}
+
+static int monahans_df_correct_data(struct mtd_info *mtd,
+		u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+{
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+			(((struct nand_chip *)(mtd->priv))->priv);
+
+	/*
+	 * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we
+	 * consider it as a ecc error which will tell the caller the
+	 * read fail We have distinguish all the errors, but the
+	 * nand_read_ecc only check this function return value
+	 */
+	if (info->retcode != ERR_NONE)
+		return -1;
+
+	return 0;
+}
+
+static void monahans_df_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	return;
+}
+
+/*
+ * The relocation table management is different between MOBM V2 and V3.
+ *
+ * MOBM V2 is applied on chips taped out before MhnLV A0.
+ * MOBM V3 is applied on chips taped out after MhnLV A0. It's also applied
+ * on MhnLV A0.
+ */
+static int calc_obm_ver(void)
+{
+	unsigned int	cpuid;
+	/* read CPU ID */
+	__asm__ (
+		"mrc p15, 0, %0, c0, c0, 0\n"
+		: "=r" (cpuid)
+	);
+	/* It's not xscale chip. */
+	if ((cpuid & 0xFFFF0000) != 0x69050000)
+		return MHN_OBM_INVAL;
+	/* It's MhnP Ax */
+	if ((cpuid & 0x0000FFF0) == 0x00006420)
+		return MHN_OBM_V2;
+	/* It's MhnP Bx */
+	if ((cpuid & 0x0000FFF0) == 0x00006820) {
+		if ((cpuid & 0x0F) <= 5)
+			return MHN_OBM_V2;
+		else
+			return MHN_OBM_V3;
+	}
+	/* It's MhnL Ax */
+	if ((cpuid & 0x0000FFF0) == 0x00006880) {
+		if ((cpuid & 0x0F) == 0)
+			return MHN_OBM_V2;
+		else
+			return MHN_OBM_V3;
+	}
+	/* It's MhnLV Ax */
+	if ((cpuid & 0x0000FFF0) == 0x00006890)
+		return MHN_OBM_V3;
+	return MHN_OBM_INVAL;
+}
+
+
+/*
+ * MOBM maintains a relocation table. It's used to replace bad blocks.
+ * If block A is bad, it will use block B instead.
+ * There're 127 relocated blocks. All of them reside in the bottom of NAND
+ * flash. So they're reserved and can't be calculated in mtd size and chip
+ * size.
+ */
+static int read_reloc_table(struct mtd_info *mtd)
+{
+	struct nand_chip *this = NULL;
+	struct monahans_dfc_info *info = NULL;
+	struct dfc_context *context = NULL;
+	struct reloc_table *table = NULL;
+	int page, maxslot;
+	int obm, valid;
+
+	obm = calc_obm_ver();
+	this = (struct nand_chip *)(mtd->priv);
+	info = (struct monahans_dfc_info *)(this->priv);
+	context = info->context;
+
+	mtd->size -= (NAND_RELOC_MAX * mtd->erasesize);
+	this->chipsize -= (NAND_RELOC_MAX << this->phys_erase_shift);
+	page = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+	this->select_chip(mtd, 0);
+	valid = 0;
+	if (obm == MHN_OBM_V2) {
+		/* On MOBM V2, the relocation table resides in the last page
+		 * of the first block.
+		 */
+		memset(info->data_buf, 0, BUFLEN);
+		monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
+	memcpy(((unsigned char *)&(info->table)), info->data_buf,
+			sizeof(struct reloc_table));
+		if (info->table.header == NAND_RELOC_HEADER)
+			valid = 1;
+	} else if (obm == MHN_OBM_V3) {
+		/* On MOBM V3, there're several relocation tables in the first
+		 * block.
+		 * When new bad blocks are found, a new relocation table will
+		 * be generated and written back to the first block. But the
+		 * original relocation table won't be erased. Even if the new
+		 * relocation table is written wrong, system can still find an
+		 * old one.
+		 * One page contains one slot.
+		 */
+		maxslot = 1 << (this->phys_erase_shift - this->page_shift);
+		page = maxslot - MAX_BBT_SLOTS;
+		for (; page < maxslot; page++) {
+			monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
+			table = (struct reloc_table *)info->data_buf;
+			if (info->retcode == ERR_NONE) {
+				if (table->header != NAND_RELOC_HEADER) {
+					continue;
+	} else {
+					memcpy(((unsigned char *)&(info->table)),
+						table, sizeof(struct reloc_table));
+					valid = 1;
+					break;
+				}
+			}
+	}
+
+	} else {
+		printk(KERN_ERR "The version of MOBM isn't supported\n");
+	}
+	if (valid) {
+		memcpy(((unsigned char *)&(info->table)), info->data_buf,
+			sizeof(struct reloc_table));
+		printk(KERN_DEBUG "relocation table at page:%d\n", page);
+		PRINT_BUF((unsigned char *)&(info->table),
+			sizeof(struct reloc_table));
+	info->table_init = 1;
+	} else {
+		/* There should be a valid relocation table slot at least. */
+		printk(KERN_ERR "NO VALID relocation table can be \
+				recognized\n");
+		printk(KERN_ERR "CAUTION: It may cause unpredicated error\n");
+		printk(KERN_ERR "Please re-initialize the NAND flash.\n");
+		memset((unsigned char *)&(info->table), 0,
+				sizeof(struct reloc_table));
+		info->table_init = 0;
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* add the relocation entry into the relocation table
+ * It's valid on MOBM V3.
+ * If the relocated block is bad, an new entry will be added into the
+ * bottom of the relocation table.
+ */
+static int update_rel_table(struct mtd_info *mtd, int block)
+{
+	struct nand_chip *this = NULL;
+	struct monahans_dfc_info *info = NULL;
+	struct reloc_table *table = NULL;
+	int obm, reloc_block;
+
+	this = (struct nand_chip *)(mtd->priv);
+	info = (struct monahans_dfc_info *)(this->priv);
+	obm = calc_obm_ver();
+	if (obm == MHN_OBM_V3) {
+		table = &info->table;
+		if (info->table_init == 0) {
+			printk(KERN_ERR "Error: the initial relocation \
+					table can't be read\n");
+			memset(table, 0, sizeof(struct reloc_table));
+			table->header = NAND_RELOC_HEADER;
+			info->table_init = 1;
+		}
+		if (table->total == 0) {
+			/* Point to the first relocated block.
+			 * It resides in the last block of flash.
+			 * the relocation entry has calculated in
+			 * chipsize
+			 */
+			reloc_block = (this->chipsize
+					>> this->phys_erase_shift)
+					+ NAND_RELOC_MAX - 1;
+		} else if (table->total < NAND_RELOC_MAX) {
+			reloc_block = table->reloc[table->total - 1].to - 1;
+		} else {
+			printk(KERN_ERR "Relocation table exceed max number, \
+				cannot mark block 0x%x as bad block\n", block);
+			return -ENOSPC;
+		}
+		/* Make sure that reloc_block is pointing to a valid block */
+		for (; ; reloc_block--) {
+			/* The relocate table is full */
+			if (reloc_block < (this->chipsize
+					>> this->phys_erase_shift))
+				return -ENOSPC;
+			this->cmdfunc(mtd, NAND_CMD_ERASE1, 0, reloc_block
+					<< (this->phys_erase_shift
+					- this->page_shift));
+			if (info->retcode == ERR_NONE)
+				break;
+		}
+		/* Create the relocated block information in the table */
+		table->reloc[table->total].from = block;
+		table->reloc[table->total].to = reloc_block;
+		table->total++;
+	}
+	return 0;
+}
+
+/* Write the relocation table back to device, if there's room. */
+static int sync_rel_table(struct mtd_info *mtd, int *idx)
+{
+	struct nand_chip *this = NULL;
+	struct monahans_dfc_info *info = NULL;
+	int obm, start_page, len;
+
+	if (*idx >= MAX_BBT_SLOTS) {
+		printk(KERN_ERR "Can't write relocation table to device \
+				any more.\n");
+		return -1;
+	}
+	if (*idx < 0) {
+		printk(KERN_ERR "Wrong Slot is specified.\n");
+		return -1;
+	}
+	this = (struct nand_chip *)(mtd->priv);
+	info = (struct monahans_dfc_info *)(this->priv);
+	len = 4;
+	len += info->table.total << 2;
+	obm = calc_obm_ver();
+	if (obm == MHN_OBM_V3) {
+		/* write to device */
+		start_page = 1 << (this->phys_erase_shift - this->page_shift);
+		start_page = start_page - 1 - *idx;
+		memset(&(info->data_buf), 0xFF, BUFLEN);
+		memcpy(&(info->data_buf), &(info->table), len);
+
+		printk(KERN_DEBUG "DUMP relocation table before write. \
+				page:0x%x\n", start_page);
+		monahans_df_command(mtd, NAND_CMD_SEQIN, 0, start_page);
+		monahans_df_command(mtd, NAND_CMD_PAGEPROG, 0, start_page);
+		/* write to idx */
+		(*idx)++;
+		/* dump it */
+		memset(&(info->data_buf), 0, BUFLEN);
+		monahans_df_command(mtd, NAND_CMD_READOOB, 0, start_page);
+		PRINT_BUF(info->data_buf, len);
+	}
+	return 0;
+}
+
+
+/* Find the relocated block of the bad one.
+ * If it's a good block, return 0. Otherwise, return a relocated one.
+ * idx points to the next relocation entry
+ * If the relocated block is bad, an new entry will be added into the
+ * bottom of the relocation table.
+ */
+static unsigned short search_rel_block(int block, struct mtd_info *mtd)
+{
+	struct nand_chip *this = NULL;
+	struct monahans_dfc_info *info = NULL;
+	struct reloc_table *table = NULL;
+	int i, max, reloc_block = 0;
+
+	this = (struct nand_chip *)(mtd->priv);
+	info = (struct monahans_dfc_info *)(this->priv);
+	table = &(info->table);
+	if ((block <= 0) || (block > this->chipsize)
+			|| (info->table_init == 0) || (table->total == 0))
+		return 0;
+	if (table->total > NAND_RELOC_MAX)
+		table->total = NAND_RELOC_MAX;
+	max = table->total;
+	for (i = 0; i < max; i++) {
+		if (block == table->reloc[i].from)
+			reloc_block = table->reloc[i].to;
+	}
+	return reloc_block;
+}
+
+/*
+ * Check whether the block is a bad one.
+ * At first, it will search the relocation table.
+ * If necessary, it will search the BBT. Because relocation table can only
+ * maintain limited record. If there're more bad blocks, they can't be
+ * recorded in relocation table. They can only be recorded in BBT.
+ */
+static int monahans_df_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	struct nand_chip *this = NULL;
+	int page, block, reloc_block, chipnr, res = 0;
+	u16 bad;
+
+	/* At here, we only support one flash chip */
+	this = (struct nand_chip *)mtd->priv;
+	block = (int)(ofs >> this->phys_erase_shift);
+	/* search the block in the relocation table */
+	reloc_block = search_rel_block(block, mtd);
+	if (reloc_block) {
+		ofs = ((reloc_block << this->phys_erase_shift) |
+			(ofs & ((1 << this->phys_erase_shift) - 1)));
+	}
+
+	/* search BBT
+	 * Maybe the relocation table is full, but some bad blocks aren't
+	 * recordered in it.
+	 * The below code are copied from nand_block_bad().
+	 */
+	if (getchip) {
+		page = (int)(ofs >> this->page_shift);
+		chipnr = (int)(ofs >> this->chip_shift);
+
+		/* Select the NAND chips */
+		this->select_chip(mtd, chipnr);
+	} else
+		page = (int)ofs;
+
+	if (this->options & NAND_BUSWIDTH_16) {
+		this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE,
+				page & this->pagemask);
+		bad = cpu_to_le16(this->read_word(mtd));
+		if (this->badblockpos & 0x1)
+			bad >>= 1;
+		if ((bad & 0xFF) != 0xFF)
+			res = 1;
+	} else {
+		this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos,
+				page & this->pagemask);
+		if (this->read_byte(mtd) != 0xFF)
+			res = 1;
+	}
+
+	return res;
+}
+
+static int monahans_df_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *this = NULL;
+	struct monahans_dfc_info *info = NULL;
+	unsigned char buf[2] = {0, 0};
+	int block, reloc_block, page, ret;
+
+	this = (struct nand_chip *)mtd->priv;
+	info = (struct monahans_dfc_info *)(this->priv);
+	/* Get block number */
+	block = ((int)ofs) >> this->bbt_erase_shift;
+	ret = update_rel_table(mtd, block);
+	if (!ret) {
+		sync_rel_table(mtd, &(info->current_slot));
+		return 0;
+		} else {
+		reloc_block = search_rel_block(block, mtd);
+		if (reloc_block)
+			block = reloc_block;
+		if (this->bbt)
+			this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+	}
+
+	/* Do we have a flash based bad block table ? */
+	if (this->options & NAND_USE_FLASH_BBT)
+		return nand_update_bbt(mtd, ofs);
+
+	/* mark the bad block flag at the first two pages */
+	page = block << (this->phys_erase_shift - this->page_shift);
+	ofs = mtd->writesize + this->badblockpos;
+	this->cmdfunc(mtd, NAND_CMD_SEQIN, ofs, page);
+	this->write_buf(mtd, buf, 2);
+	this->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+	page++;
+	this->cmdfunc(mtd, NAND_CMD_SEQIN, ofs, page);
+	this->write_buf(mtd, buf, 2);
+	this->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+	return 0;
+}
+
+static int dump_bbt_flash(struct mtd_info *mtd)
+{
+	struct nand_chip *this = NULL;
+	struct monahans_dfc_info *info = NULL;
+	int block, page, totlen;
+
+	this = (struct nand_chip *)mtd->priv;
+	info = (struct monahans_dfc_info *)this->priv;
+	block = (this->chipsize >> this->phys_erase_shift) - 1;
+	totlen = (this->chipsize >> this->phys_erase_shift) >> 2;
+	printk(KERN_ERR "totlen:0x%x\n", totlen);
+	this->select_chip(mtd, 0);
+	if (this->bbt_td) {
+		printk(KERN_ERR "BBT page:0x%x\n", this->bbt_td->pages[0]);
+		page = this->bbt_td->pages[0];
+		if (this->bbt_td->pages[0] <= 0) {
+			page = block << (this->phys_erase_shift
+				- this->page_shift);
+		}
+		while (totlen > 0) {
+			printk(KERN_ERR "page:0x%x\n", page);
+			monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
+			printk(KERN_ERR "read result:0x%x\n", info->retcode);
+			PRINT_BUF(info->data_buf, BUFLEN);
+			totlen -= (1 << this->page_shift);
+			page++;
+		}
+	}
+	if (this->bbt_md) {
+		printk(KERN_ERR "BBT page:0x%x\n", this->bbt_md->pages[0]);
+		page = this->bbt_md->pages[0];
+		if (this->bbt_td->pages[0] <= 0) {
+			page = block << (this->phys_erase_shift
+				- this->page_shift);
+			}
+		while (totlen > 0) {
+			printk(KERN_ERR "page:0x%x\n", page);
+			monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
+			printk(KERN_ERR "read result:0x%x\n", info->retcode);
+			PRINT_BUF(info->data_buf, BUFLEN);
+			totlen -= (1 << this->page_shift);
+			page++;
+		}
+
+	}
+	return 0;
+}
+
+static int dump_bbt_mem(struct mtd_info *mtd)
+{
+	struct nand_chip *this = NULL;
+
+	this = (struct nand_chip *)mtd->priv;
+	PRINT_BUF(this->bbt, 225);
+	return 0;
+}
+
+static int monahans_df_scan_bbt(struct mtd_info *mtd)
+{
+	struct nand_chip *this = NULL;
+	int ret;
+
+	this = (struct nand_chip *)mtd->priv;
+	ret = read_reloc_table(mtd);
+	if (ret) {
+		printk(KERN_ERR "Failed to get relocation table\n");
+		printk(KERN_ERR "Try to build a new BBT. It may result \
+				unpredicated error.\n");
+		/* Create new memory based and flash based BBT */
+	}
+	nand_scan_bbt(mtd, &monahans_bbt_default);
+	//dump_bbt_flash(mtd);
+	dump_bbt_mem(mtd);
+	return 0;
+#if 0
+	/* Read flashed based BBT from device */
+	return (nand_scan_bbt(mtd, &monahans_bbt_main));
+#endif
+}
+
+
+static int monahans_df_probe(struct platform_device *pdev)
+{
+	struct nand_chip *this;
+	struct monahans_dfc_info *info;
+	int status = -1;
+	unsigned int data_buf_len;
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+	unsigned int buf_len;
+#endif
+	int i, ret = 0;
+
+	printk(KERN_ERR "Nand driver probe\n");
+
+	dfc_context.membase = ioremap_nocache(0x43100000, 0x100000);
+	if (!dfc_context.membase)
+		printk(KERN_ERR "Couldn't ioremap\n");
+
+	pxa_set_cken(CKEN_NAND, 1);
+
+	for (i = DFC_FLASH_NULL + 1; i < DFC_FLASH_END; i++)
+	{
+		uint32_t id;
+
+		status = dfc_init(&dfc_context, i);
+		if (status)
+			continue;
+		status = dfc_readid(&dfc_context, &id);
+		if (status)
+			continue;
+		printk(KERN_DEBUG "id:0x%x, chipid:0x%x\n",
+			id, dfc_context.flash_info->chip_id);
+		if (id == dfc_context.flash_info->chip_id)
+			break;
+	}
+
+	if(i == DFC_FLASH_END) {
+		printk(KERN_ALERT "Monahans NAND device:"
+			"Nand Flash initialize failure!\n");
+		ret = -ENXIO;
+		goto out;
+	}
+	flash_config = i;
+
+	monahans_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) +
+			sizeof(struct monahans_dfc_info) , GFP_KERNEL);
+	if (!monahans_mtd) {
+		printk (KERN_ERR "Monahans NAND device:"
+			"Unable to allocate NAND MTD device structure.\n");
+		ret = -ENOMEM;
+		goto out;
+        }
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *)((void *)monahans_mtd + sizeof(struct mtd_info));
+	info = (struct monahans_dfc_info *)((void *)this + sizeof(struct nand_chip));
+	dfc_context.mtd = monahans_mtd;
+
+	monahans_mtd->priv = this;
+	this->priv = info;
+	data_buf_len = dfc_context.flash_info->page_size +
+		dfc_context.flash_info->oob_size;
+	info->state = STATE_READY;
+	init_completion(&info->cmd_complete);
+	info->table_init = 0;
+	memset(&info->table, 0x0, sizeof(struct reloc_table));
+	printk(KERN_DEBUG "%s: this->controller: 0x%x, &this->controller: 0x%x\n",__func__, (unsigned int)this->controller, (unsigned int)&(this->controller));
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+	info->dma_mask = 0xffffffffUL;
+
+	dev->dma_mask = &info->dma_mask;
+	dev->coherent_dma_mask = 0xffffffffUL;
+
+	/* alloc dma data buffer for data
+	 * buffer + 2*descriptor + command buffer
+	 */
+	buf_len = ALIGN(2*sizeof(struct pxa_dma_desc), 32) +
+		ALIGN(data_buf_len, 32) + ALIGN(NAND_CMD_DMA_LEN, 32);
+
+	printk(KERN_INFO "Try to allocate dma buffer(len:%d)"
+		"for data buffer + 2*descriptor + command buffer\n", buf_len);
+	info->data_desc = (struct pxa_dma_desc*)dma_alloc_writecombine(dev,
+			buf_len, &info->data_desc_addr, GFP_KERNEL);
+	if (!info->data_desc) {
+		printk(KERN_ERR "Monahans NAND device:"
+			"Unable to alloc dma buffer\n");
+		ret = -ENOMEM;
+		goto free_mtd;
+	}
+
+	info->cmd_desc = (struct pxa_dma_desc*)((char *)info->data_desc +
+			sizeof(struct pxa_dma_desc));
+	info->cmd_desc_addr = (dma_addr_t)((char *)info->data_desc_addr +
+			sizeof(struct pxa_dma_desc));
+	info->data_buf = (char *)info->data_desc +
+		ALIGN(2*sizeof(struct pxa_dma_desc), 32);
+	info->data_buf_addr = (dma_addr_t)((char *)info->data_desc_addr +
+		ALIGN(2*sizeof(struct pxa_dma_desc), 32));
+	info->cmd_buf = (char *)info->data_buf + ALIGN(data_buf_len, 32);
+	info->cmd_buf_addr = (dma_addr_t)((char *)info->data_buf_addr +
+			ALIGN(data_buf_len, 32));
+
+	D1(printk("Get dma buffer for data dma descriptor, virt:0x%x, phys0x:%x\n",
+		(unsigned int)info->data_desc, info->data_desc_addr));
+	D1(printk("Get dma buffer for command dma descriptors, virt:0x%x,"
+		"phys0x:%x\n", (unsigned int)info->cmd_desc, info->cmd_desc_addr));
+	D1(printk("Get dma buffer for data, virt:0x%x, phys0x:%x\n",
+		(unsigned int)info->data_buf, info->data_buf_addr));
+	D1(printk("Get dma buffer for command, virt:0x%x, phys0x:%x\n",
+		(unsigned int)info->cmd_buf, info->cmd_buf_addr));
+
+	D1(printk("Try to allocate dma channel for data\n"));
+
+	info->data_dma = pxa_request_dma("NAND DATA", DMA_PRIO_LOW,
+			monahans_dfc_data_dma_irq, info);
+	if (info->data_dma < 0) {
+		printk(KERN_ERR "Monahans NAND device:"
+			"Unable to alloc dma channel for data\n");
+		ret = info->data_dma;
+		goto free_buf;
+	}
+	D1(printk("Get dma channel:%d for data\n", info->data_dma));
+
+	D1(printk("Try to allocate dma channel for command\n"));
+	info->cmd_dma = pxa_request_dma("NAND CMD", DMA_PRIO_LOW,
+			monahans_dfc_cmd_dma_irq, info);
+	if (info->cmd_dma < 0) {
+		printk(KERN_ERR "Monahans NAND device:"
+			"Unable to alloc dma channel for command\n");
+		ret = info->cmd_dma;
+		goto free_data_dma;
+	}
+	D1(printk("Get dma channel:%d for command\n", info->cmd_dma));
+
+	dfc_context.cmd_dma_ch  = info->cmd_dma;
+	dfc_context.data_dma_ch = info->data_dma;
+#else
+	printk(KERN_DEBUG "Try to allocate data buffer(len:%d)\n", data_buf_len);
+	info->data_buf = kmalloc(data_buf_len, GFP_KERNEL);
+	if (!info->data_buf) {
+		printk(KERN_ERR "Monahans NAND device:"
+			"Unable to alloc data buffer\n");
+		ret = -ENOMEM;
+		goto free_mtd;
+	}
+#endif
+
+	D1(printk("Try to request irq:%d\n", IRQ_NAND));
+	ret = request_irq(IRQ_NAND, monahans_dfc_irq, 0, pdev->name, info);
+	if (ret < 0) {
+		printk(KERN_ERR "Monahans NAND device: Unable to request irq\n");
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+		goto free_cmd_dma;
+#else
+		goto free_buf;
+#endif
+	}
+
+	D1(printk("Success request irq\n"));
+
+	/* set address of NAND IO lines */
+	this->options = (dfc_context.flash_info->flash_width == 16)? \
+			NAND_BUSWIDTH_16: 0 | NAND_USE_FLASH_BBT;
+
+	/* this->IO_ADDR_R = this->IO_ADDR_W = NDDB */
+	this->waitfunc = monahans_df_waitfunc;
+	this->select_chip = monahans_df_select_chip;
+	this->dev_ready = monahans_df_dev_ready;
+	this->cmdfunc = monahans_df_command;
+	this->read_word= monahans_df_read_word;
+	/*this->write_word= monahans_df_write_word;*/
+	this->read_byte = monahans_df_read_byte;
+	this->read_buf = monahans_df_read_buf;
+	this->write_buf = monahans_df_write_buf;
+	this->verify_buf = monahans_df_verify_buf;
+	this->ecc.hwctl = monahans_df_enable_hwecc;
+	this->ecc.calculate = monahans_df_calculate_ecc;
+	this->ecc.correct = monahans_df_correct_data;
+	this->block_bad = monahans_df_block_bad;
+	this->block_markbad = monahans_df_block_markbad;
+	this->scan_bbt = monahans_df_scan_bbt;
+	this->chip_delay= 25;
+	this->bbt_td = &monahans_bbt_main;
+	this->bbt_md = &monahans_bbt_mirror;
+
+	/* If the NAND flash is small block flash, only 512-byte pagesize
+	 * is supported.
+	 * Adjust parameters of BBT what is depended on large block nand
+	 * flash or small block nand flash.
+	 */
+	if (dfc_context.flash_info->oob_size > 16) {
+		this->ecc.layout = &monahans_lb_nand_oob;
+		this->ecc.mode = NAND_ECC_HW;
+		this->ecc.size = 2048;
+		this->ecc.bytes = 24;
+		this->bbt_td->offs = 2;
+		this->bbt_td->veroffs = 6;
+		this->bbt_md->offs = 2;
+		this->bbt_md->veroffs = 6;
+		this->badblockpos = NAND_LARGE_BADBLOCK_POS;
+		monahans_bbt_default.offs = NAND_LARGE_BADBLOCK_POS;
+		monahans_bbt_default.len = 2;
+		/* when scan_bbt() is executed, bbt version can get */
+		monahans_bbt_default.veroffs = 2;
+	} else {
+		this->ecc.layout = &monahans_sb_nand_oob;
+		this->ecc.mode = NAND_ECC_HW;
+		this->ecc.size = 512;
+		this->ecc.bytes = 6;
+		this->bbt_td->offs = 8;
+		this->bbt_td->veroffs = 12;
+		this->bbt_md->offs = 8;
+		this->bbt_md->veroffs = 12;
+		this->badblockpos = NAND_SMALL_BADBLOCK_POS;
+		monahans_bbt_default.offs = NAND_SMALL_BADBLOCK_POS;
+		monahans_bbt_default.len = 1;
+		monahans_bbt_default.veroffs = 8;
+	}
+
+	info->context = &dfc_context;
+	/* TODO: allocate dma buffer and channel */
+
+	platform_set_drvdata(pdev, monahans_mtd);
+
+	if (nand_scan(monahans_mtd, 1)) {
+		printk(KERN_ERR "Nand scan failed\n");
+		ret = -ENXIO;
+		goto free_irq;
+	}
+
+	/* There is a potential limitation that no more partition can be
+	 * added between MassStorage and BBT(last block).
+	 *
+	 * The last 127 blocks is reserved for relocation table, they aren't
+	 * statistical data of mtd size and chip size.
+	 *
+	 * BBT partitions contains 4 blocks. Two blocks are used to store
+	 * main descriptor, the other two are used to store mirror descriptor.
+	 */
+	partition_info[PART_NUM - 1].size = (monahans_bbt_main.maxblocks
+					+ monahans_bbt_mirror.maxblocks)
+					<< this->phys_erase_shift;
+	partition_info[PART_NUM - 1].offset = this->chipsize
+					- partition_info[PART_NUM - 1].size;
+	partition_info[PART_NUM - 2].offset = partition_info[PART_NUM - 3].offset
+					+ partition_info[PART_NUM - 3].size;
+	partition_info[PART_NUM - 2].size = this->chipsize
+					- partition_info[PART_NUM - 2].offset
+					- partition_info[PART_NUM - 1].size;
+	add_mtd_partitions(monahans_mtd, partition_info, PART_NUM);
+
+#ifdef CONFIG_DVFM
+        dvfm_notifier.client_data = info;
+        mhn_fv_register_notifier(&dvfm_notifier);
+#endif
+
+	return 0;
+
+free_irq:
+	free_irq(IRQ_NAND, info);
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+free_cmd_dma:
+	pxa_free_dma(info->cmd_dma);
+free_data_dma:
+	pxa_free_dma(info->data_dma);
+free_buf:
+	dma_free_writecombine(dev, buf_len, info->data_desc, info->data_desc_addr);
+#else
+free_buf:
+	kfree(info->data_buf);
+#endif
+free_mtd:
+	kfree(monahans_mtd);
+out:
+	return ret;
+
+}
+
+static int __devexit monahans_df_remove(struct platform_device *dev)
+{
+	struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+			(((struct nand_chip *)(mtd->priv))->priv);
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+	unsigned int data_buf_len = dfc_context.flash_info->page_size +
+			dfc_context.flash_info->oob_size;
+	unsigned int buf_len = ALIGN(2*sizeof(struct pxa_dma_desc), 32) +
+			ALIGN(data_buf_len, 32) + ALIGN(NAND_CMD_DMA_LEN, 32);
+#endif
+
+#ifdef CONFIG_DVFM
+        mhn_fv_unregister_notifier(&dvfm_notifier);
+#endif
+
+	platform_set_drvdata(dev, NULL);
+
+	del_mtd_device(mtd);
+	del_mtd_partitions(mtd);
+	free_irq(IRQ_NAND, info);
+#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
+	pxa_free_dma(info->cmd_dma);
+	pxa_free_dma(info->data_dma);
+	dma_free_writecombine(dev, buf_len, info->data_desc,
+		info->data_desc_addr);
+#else
+	kfree(info->data_buf);
+#endif
+	kfree(mtd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int monahans_df_suspend(struct platform_device *dev, pm_message_t state, u32 level)
+{
+	struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+			(((struct nand_chip *)(mtd->priv))->priv);
+
+	if( SUSPEND_DISABLE == level){ /*SUSPEND_NOTIFY*/
+		if (info->state != STATE_READY) {
+			printk(KERN_ERR "current state is %d\n", info->state);
+			return -EAGAIN;
+		}
+		info->state = STATE_SUSPENDED;
+		/*
+		 * The PM code need read the mobm from NAND.
+		 * So the NAND clock can't be stop here.
+		 * The PM code will cover this.
+		 */
+		/* pxa_set_cken(CKEN_NAND, 0); */
+	}
+	return 0;
+}
+
+static int monahans_df_resume(struct platform_device *dev, u32 level)
+{
+	struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
+	struct monahans_dfc_info *info = (struct monahans_dfc_info *)
+			(((struct nand_chip *)(mtd->priv))->priv);
+	int status;
+
+	if(RESUME_ENABLE == level){
+		if (info->state != STATE_SUSPENDED)
+			printk(KERN_WARNING "Error State after resume back\n");
+
+		info->state = STATE_READY;
+
+		pxa_set_cken(CKEN_NAND, 1);
+
+		status = dfc_init(&dfc_context, flash_config);
+		if (status) {
+			printk(KERN_ALERT "Monahans NAND device:"
+				"Nand Flash initialize failure!\n");
+			return -ENXIO;
+		}
+	}
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_DVFM
+static int mhn_nand_dvfm_notifier(unsigned cmd, void *client_data, void *info)
+{
+	struct monahans_dfc_info *dfc_info =
+			(struct monahans_dfc_info *)client_data;
+
+	switch (cmd) {
+	case FV_NOTIFIER_QUERY_SET :
+		if (dfc_info->state != STATE_READY)
+			return -1;
+		break;
+
+	case FV_NOTIFIER_PRE_SET :
+		break;
+
+	case FV_NOTIFIER_POST_SET :
+		break;
+	}
+
+	return 0;
+}
+#endif
+
+static struct platform_driver monahans_df_driver = {
+	.probe		= monahans_df_probe,
+	.remove		= __devexit_p(monahans_df_remove),
+#ifdef CONFIG_PM
+	.suspend	= monahans_df_suspend,
+	.resume		= monahans_df_resume,
+#endif
+	.driver		= {
+		.name		= "monahans-nand-flash",
+	}
+};
+
+static void __exit monahans_df_cleanup(void)
+{
+	printk(KERN_ERR "Nand driver registered\n");
+	platform_driver_unregister(&monahans_df_driver);
+}
+
+static int __init monahans_df_init(void)
+{
+	return platform_driver_register(&monahans_df_driver);
+}
+
+module_init(monahans_df_init);
+module_exit(monahans_df_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jingqing.xu (jingqing.xu@intel.com)");
+MODULE_DESCRIPTION("Glue logic layer for NAND flash on monahans DFC");
+
+
Index: linux-2.6.23/arch/arm/mach-pxa/zylonite.c
===================================================================
--- linux-2.6.23.orig/arch/arm/mach-pxa/zylonite.c	2008-02-13 00:59:45.000000000 +0000
+++ linux-2.6.23/arch/arm/mach-pxa/zylonite.c	2008-02-13 09:11:02.000000000 +0000
@@ -29,6 +29,8 @@
 #include "generic.h"
 
 int gpio_backlight;
+int gpio_vsync;
+int gpio_vsync1;
 int gpio_eth_irq;
 
 int lcd_id;
@@ -54,6 +56,16 @@
 	.resource	= smc91x_resources,
 };
 
+static struct platform_device nand_device = {
+	.name		= "monahans-nand-flash",
+	.id		= -1,
+};
+
+static struct platform_device touch_device = {
+	.name		= "pxa2xx-touch",
+	.id		= -1,
+};
+
 #if defined(CONFIG_FB_PXA) || (CONFIG_FB_PXA_MODULES)
 static void zylonite_backlight_power(int on)
 {
@@ -96,7 +108,7 @@
 };
 
 static struct pxafb_mode_info sharp_ls037_modes[] = {
-	[0] = {
+	[1] = {
 		.pixclock	= 158000,
 		.xres		= 240,
 		.yres		= 320,
@@ -109,8 +121,8 @@
 		.lower_margin	= 3,
 		.sync		= 0,
 	},
-	[1] = {
-		.pixclock	= 39700,
+	[0] = {
+		.pixclock	= 45000,
 		.xres		= 480,
 		.yres		= 640,
 		.bpp		= 16,
@@ -137,6 +149,11 @@
 	/* backlight GPIO: output, default on */
 	gpio_direction_output(gpio_backlight, 1);
 
+	gpio_direction_output(gpio_vsync, 0);
+	gpio_direction_output(gpio_vsync1, 0);
+
+	printk(KERN_ERR "LCD ID is %x\n", lcd_id);
+
 	if (lcd_id & 0x20) {
 		set_pxa_fb_info(&zylonite_sharp_lcd_info);
 		return;
@@ -169,6 +186,8 @@
 	smc91x_resources[1].start = gpio_to_irq(gpio_eth_irq);
 	smc91x_resources[1].end   = gpio_to_irq(gpio_eth_irq);
 	platform_device_register(&smc91x_device);
+	platform_device_register(&nand_device);
+	platform_device_register(&touch_device);
 
 	zylonite_init_lcd();
 }
Index: linux-2.6.23/arch/arm/mach-pxa/zylonite_pxa300.c
===================================================================
--- linux-2.6.23.orig/arch/arm/mach-pxa/zylonite_pxa300.c	2008-02-13 00:59:45.000000000 +0000
+++ linux-2.6.23/arch/arm/mach-pxa/zylonite_pxa300.c	2008-02-13 14:01:13.000000000 +0000
@@ -62,12 +62,12 @@
 	GPIO110_UART3_RXD,
 
 	/* AC97 */
-	GPIO23_AC97_nACRESET,
+	/*GPIO23_AC97_nACRESET,
 	GPIO24_AC97_SYSCLK,
 	GPIO29_AC97_BITCLK,
 	GPIO25_AC97_SDATA_IN_0,
 	GPIO27_AC97_SDATA_OUT,
-	GPIO28_AC97_SYNC,
+	GPIO28_AC97_SYNC,*/
 
 	/* Keypad */
 	GPIO107_KP_DKIN_0,
@@ -104,6 +104,41 @@
 	/* Ethernet */
 	GPIO2_nCS3,
 	GPIO99_GPIO,
+
+	/* NAND */
+	MFP_CFG_X(DF_INT_RnB, AF0, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nRE_nOE, AF1, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nWE, AF1, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_CLE_nOE, AF0, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nADV1_ALE, AF1, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nCS0, AF1, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_nCS1, AF0, DS10X, PULL_LOW),
+	MFP_CFG_X(DF_IO0, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO1, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO2, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO3, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO4, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO5, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO6, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO7, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO8, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO9, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO10, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO11, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO12, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO13, AF1, DS08X, PULL_LOW),
+	MFP_CFG_X(DF_IO14, AF1, DS08X, PULL_LOW),
+
+	/* AC97 */
+	MFP_CFG_X(GPIO23, AF1, DS03X, PULL_LOW),
+	MFP_CFG_X(GPIO27, AF1, DS03X, PULL_LOW),
+	MFP_CFG_X(GPIO28, AF1, DS03X, PULL_LOW),
+	MFP_CFG_X(GPIO29, AF1, DS03X, PULL_LOW),
+	MFP_CFG_X(GPIO25, AF1, DS03X, PULL_LOW),
+
+	MFP_CFG_X(GPIO26, AF0, DS01X, PULL_LOW), /* Interrupt */
+	MFP_CFG_X(GPIO24, AF0, DS03X, PULL_LOW), /*SYSCLK external */
+	MFP_CFG_X(GPIO11, AF0, DS01X, PULL_LOW),
 };
 
 static mfp_cfg_t pxa310_mfp_cfg[] __initdata = {
@@ -163,6 +198,9 @@
 		pxa3xx_mfp_write(lcd_detect_pins[i], mfpr_save[i]);
 }
 
+extern int gpio_vsync;
+extern int gpio_vsync1;
+
 void __init zylonite_pxa300_init(void)
 {
 	if (cpu_is_pxa300() || cpu_is_pxa310()) {
@@ -174,6 +212,8 @@
 
 		/* GPIO pin assignment */
 		gpio_backlight = mfp_to_gpio(MFP_PIN_GPIO20);
+		gpio_vsync = mfp_to_gpio(GPIO76_LCD_VSYNC);
+		gpio_vsync1 = mfp_to_gpio(GPIO71_LCD_LDD_17);
 	}
 
 	if (cpu_is_pxa300()) {
Index: linux-2.6.23/drivers/video/pxafb.c
===================================================================
--- linux-2.6.23.orig/drivers/video/pxafb.c	2008-02-13 00:59:45.000000000 +0000
+++ linux-2.6.23/drivers/video/pxafb.c	2008-02-13 00:59:45.000000000 +0000
@@ -1543,9 +1543,9 @@
         if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK)
                 dev_warn(&dev->dev, "machine LCCR0 setting contains illegal bits: %08x\n",
                         inf->lccr0 & LCCR0_INVALID_CONFIG_MASK);
-        if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)
-                dev_warn(&dev->dev, "machine LCCR3 setting contains illegal bits: %08x\n",
-                        inf->lccr3 & LCCR3_INVALID_CONFIG_MASK);
+        //if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)
+        //        dev_warn(&dev->dev, "machine LCCR3 setting contains illegal bits: %08x\n",
+        //                inf->lccr3 & LCCR3_INVALID_CONFIG_MASK);
         if (inf->lccr0 & LCCR0_DPD &&
 	    ((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas ||
 	     (inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl ||
Index: linux-2.6.23/include/asm-arm/arch-pxa/mfp-pxa300.h
===================================================================
--- linux-2.6.23.orig/include/asm-arm/arch-pxa/mfp-pxa300.h	2008-02-13 00:59:45.000000000 +0000
+++ linux-2.6.23/include/asm-arm/arch-pxa/mfp-pxa300.h	2008-02-13 00:59:45.000000000 +0000
@@ -175,13 +175,13 @@
 #define GPIO68_LCD_LDD_14	MFP_CFG_DRV(GPIO68, AF1, DS01X)
 #define GPIO69_LCD_LDD_15	MFP_CFG_DRV(GPIO69, AF1, DS01X)
 #define GPIO70_LCD_LDD_16	MFP_CFG_DRV(GPIO70, AF1, DS01X)
-#define GPIO71_LCD_LDD_17	MFP_CFG_DRV(GPIO71, AF1, DS01X)
+#define GPIO71_LCD_LDD_17	MFP_CFG_DRV(GPIO71, AF0, DS01X)
 #define GPIO62_LCD_CS_N		MFP_CFG_DRV(GPIO62, AF2, DS01X)
 #define GPIO72_LCD_FCLK		MFP_CFG_DRV(GPIO72, AF1, DS01X)
 #define GPIO73_LCD_LCLK		MFP_CFG_DRV(GPIO73, AF1, DS01X)
 #define GPIO74_LCD_PCLK		MFP_CFG_DRV(GPIO74, AF1, DS01X)
 #define GPIO75_LCD_BIAS		MFP_CFG_DRV(GPIO75, AF1, DS01X)
-#define GPIO76_LCD_VSYNC	MFP_CFG_DRV(GPIO76, AF2, DS01X)
+#define GPIO76_LCD_VSYNC	MFP_CFG_DRV(GPIO76, AF0, DS01X)
 
 #define GPIO15_LCD_CS_N		MFP_CFG_DRV(GPIO15,  AF2, DS01X)
 #define GPIO127_LCD_CS_N	MFP_CFG_DRV(GPIO127, AF1, DS01X)