Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl
index a8c8cce..6fbc41d 100644
--- a/Documentation/DocBook/mtdnand.tmpl
+++ b/Documentation/DocBook/mtdnand.tmpl
@@ -275,16 +275,13 @@
 	int err = 0;
 
 	/* Allocate memory for MTD device structure and private data */
-	board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
+	board_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!board_mtd) {
 		printk ("Unable to allocate NAND MTD device structure.\n");
 		err = -ENOMEM;
 		goto out;
 	}
 
-	/* Initialize structures */
-	memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip));
-
 	/* map physical adress */
 	baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024);
 	if(!baseaddr){
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index fbec8cd..8848e8a 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -278,6 +278,14 @@
 	  This enables read only access to SmartMedia formatted NAND
 	  flash. You can mount it with FAT file system.
 
+config MTD_OOPS
+	tristate "Log panic/oops to an MTD buffer"
+	depends on MTD
+	help
+	  This enables panic and oops messages to be logged to a circular
+	  buffer in a flash partition where it can be read back at some
+	  later point.
+
 source "drivers/mtd/chips/Kconfig"
 
 source "drivers/mtd/maps/Kconfig"
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 6d958a4..7f0b04b 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -22,6 +22,7 @@
 obj-$(CONFIG_INFTL)		+= inftl.o
 obj-$(CONFIG_RFD_FTL)		+= rfd_ftl.o
 obj-$(CONFIG_SSFDC)		+= ssfdc.o
+obj-$(CONFIG_MTD_OOPS)		+= mtdoops.o
 
 nftl-objs		:= nftlcore.o nftlmount.o
 inftl-objs		:= inftlcore.o inftlmount.o
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 2f19fa7..3aa3dca 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -526,7 +526,7 @@
 	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 
 	/*
-	 * Probing of multi-partition flash ships.
+	 * Probing of multi-partition flash chips.
 	 *
 	 * To support multiple partitions when available, we simply arrange
 	 * for each of them to have their own flchip structure even if they
@@ -653,7 +653,7 @@
  resettime:
 	timeo = jiffies + HZ;
  retry:
-	if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {
+	if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE || mode == FL_SHUTDOWN)) {
 		/*
 		 * OK. We have possibility for contension on the write/erase
 		 * operations which are global to the real chip and not per
@@ -798,6 +798,9 @@
 		if (mode == FL_READY && chip->oldstate == FL_READY)
 			return 0;
 
+	case FL_SHUTDOWN:
+		/* The machine is rebooting now,so no one can get chip anymore */
+		return -EIO;
 	default:
 	sleep:
 		set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1166,28 +1169,34 @@
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	unsigned long ofs;
+	unsigned long ofs, last_end = 0;
 	int chipnum;
 	int ret = 0;
 
 	if (!map->virt || (from + len > mtd->size))
 		return -EINVAL;
 
-	*mtdbuf = (void *)map->virt + from;
-	*retlen = 0;
-
 	/* Now lock the chip(s) to POINT state */
 
 	/* ofs: offset within the first chip that the first read should start */
 	chipnum = (from >> cfi->chipshift);
 	ofs = from - (chipnum << cfi->chipshift);
 
+	*mtdbuf = (void *)map->virt + cfi->chips[chipnum].start + ofs;
+	*retlen = 0;
+
 	while (len) {
 		unsigned long thislen;
 
 		if (chipnum >= cfi->numchips)
 			break;
 
+		/* We cannot point across chips that are virtually disjoint */
+		if (!last_end)
+			last_end = cfi->chips[chipnum].start;
+		else if (cfi->chips[chipnum].start != last_end)
+			break;
+
 		if ((len + ofs -1) >> cfi->chipshift)
 			thislen = (1<<cfi->chipshift) - ofs;
 		else
@@ -1201,6 +1210,7 @@
 		len -= thislen;
 
 		ofs = 0;
+		last_end += 1 << cfi->chipshift;
 		chipnum++;
 	}
 	return 0;
@@ -1780,7 +1790,7 @@
 	return ret;
 }
 
-int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
 {
 	unsigned long ofs, len;
 	int ret;
@@ -1930,7 +1940,7 @@
 	printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
 	       __FUNCTION__, ofs, len);
 	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
-		ofs, len, 0);
+		ofs, len, NULL);
 #endif
 
 	ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
@@ -1940,7 +1950,7 @@
 	printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
 	       __FUNCTION__, ret);
 	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
-		ofs, len, 0);
+		ofs, len, NULL);
 #endif
 
 	return ret;
@@ -1954,7 +1964,7 @@
 	printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
 	       __FUNCTION__, ofs, len);
 	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
-		ofs, len, 0);
+		ofs, len, NULL);
 #endif
 
 	ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
@@ -1964,7 +1974,7 @@
 	printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
 	       __FUNCTION__, ret);
 	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
-		ofs, len, 0);
+		ofs, len, NULL);
 #endif
 
 	return ret;
@@ -2255,7 +2265,7 @@
 			adr = region->offset + block * len;
 
 			status = cfi_varsize_frob(mtd,
-					do_getlockstatus_oneblock, adr, len, 0);
+					do_getlockstatus_oneblock, adr, len, NULL);
 			if (status)
 				set_bit(block, region->lockmap);
 			else
@@ -2402,10 +2412,10 @@
 		   and switch to array mode so any bootloader in
 		   flash is accessible for soft reboot. */
 		spin_lock(chip->mutex);
-		ret = get_chip(map, chip, chip->start, FL_SYNCING);
+		ret = get_chip(map, chip, chip->start, FL_SHUTDOWN);
 		if (!ret) {
 			map_write(map, CMD(0xff), chip->start);
-			chip->state = FL_READY;
+			chip->state = FL_SHUTDOWN;
 		}
 		spin_unlock(chip->mutex);
 	}
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 1f64458..389acc6 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -1609,7 +1609,7 @@
 }
 
 
-int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
 {
 	unsigned long ofs, len;
 	int ret;
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index 58e561e..a67b23b 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -17,7 +17,6 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
-#include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -70,6 +69,7 @@
 
 /* Fujitsu */
 #define MBM29F040C	0x00A4
+#define MBM29F800BA	0x2258
 #define MBM29LV650UE	0x22D7
 #define MBM29LV320TE	0x22F6
 #define MBM29LV320BE	0x22F9
@@ -129,6 +129,7 @@
 #define LH28F640BF	0x00b0
 
 /* ST - www.st.com */
+#define M29F800AB	0x0058
 #define M29W800DT	0x00D7
 #define M29W800DB	0x005B
 #define M29W160DT	0x22C4
@@ -646,6 +647,23 @@
 		}
 	}, {
 		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29F800BA,
+		.name		= "Fujitsu MBM29F800BA",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,15),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
 		.dev_id		= MBM29LV650UE,
 		.name		= "Fujitsu MBM29LV650UE",
 		.uaddr		= {
@@ -1510,6 +1528,23 @@
                        ERASEINFO(0x1000,256)
                }
 
+	}, {
+		.mfr_id		= MANUFACTURER_ST,
+		.dev_id		= M29F800AB,
+		.name		= "ST M29F800AB",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,15),
+		}
        }, {
 		.mfr_id		= MANUFACTURER_ST,	/* FIXME - CFI device? */
 		.dev_id		= M29W800DT,
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index ff642f8..811d56f 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -60,21 +60,22 @@
 	  Sometimes DataFlash chips are packaged inside MMC-format
 	  cards; at this writing, the MMC stack won't handle those.
 
-config MTD_DATAFLASH26
-	tristate "AT91RM9200 DataFlash AT26xxx"
-	depends on MTD && ARCH_AT91RM9200 && AT91_SPI
-	help
-	  This enables access to the DataFlash chip (AT26xxx) on an
-	  AT91RM9200-based board.
-	  If you have such a board and such a DataFlash, say 'Y'.
-
 config MTD_M25P80
-	tristate "Support for M25 SPI Flash"
+	tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
 	depends on SPI_MASTER && EXPERIMENTAL
 	help
-	  This enables access to ST M25P80 and similar SPI flash chips,
-	  used for program and data storage.  Set up your spi devices
-	  with the right board-specific platform data.
+	  This enables access to most modern SPI flash chips, used for
+	  program and data storage.   Series supported include Atmel AT26DF,
+	  Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X.  Other chips
+	  are supported as well.  See the driver source for the current list,
+	  or to add other chips.
+
+	  Note that the original DataFlash chips (AT45 series, not AT26DF),
+	  need an entirely different driver.
+
+	  Set up your spi devices with the right board-specific platform data,
+	  if you want to specify device partitioning or to use a device which
+	  doesn't support the JEDEC ID instruction.
 
 config MTD_SLRAM
 	tristate "Uncached system RAM"
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 8ab568b..0f788d5 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,5 +16,4 @@
 obj-$(CONFIG_MTD_LART)		+= lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)	+= mtd_dataflash.o
-obj-$(CONFIG_MTD_DATAFLASH26)	+= at91_dataflash26.o
 obj-$(CONFIG_MTD_M25P80)	+= m25p80.o
diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c
deleted file mode 100644
index 64ce37f..0000000
--- a/drivers/mtd/devices/at91_dataflash26.c
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder)
- * This is a largely modified version of at91_dataflash.c that
- * supports AT26xxx dataflash chips. The original driver supports
- * AT45xxx chips.
- *
- * Note: This driver was only tested with an AT26F004. It should be
- * easy to make it work with other AT26xxx dataflash devices, though.
- *
- * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de>
- * original Copyright (C) SAN People (Pty) Ltd
- *
- * 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.
-*/
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/mtd/mtd.h>
-
-#include <asm/arch/at91_spi.h>
-
-#define DATAFLASH_MAX_DEVICES	4	/* max number of dataflash devices */
-
-#define MANUFACTURER_ID_ATMEL		0x1F
-
-/* command codes */
-
-#define AT26_OP_READ_STATUS		0x05
-#define AT26_OP_READ_DEV_ID		0x9F
-#define AT26_OP_ERASE_PAGE_4K		0x20
-#define AT26_OP_READ_ARRAY_FAST		0x0B
-#define AT26_OP_SEQUENTIAL_WRITE	0xAF
-#define AT26_OP_WRITE_ENABLE		0x06
-#define AT26_OP_WRITE_DISABLE		0x04
-#define AT26_OP_SECTOR_PROTECT		0x36
-#define AT26_OP_SECTOR_UNPROTECT	0x39
-
-/* status register bits */
-
-#define AT26_STATUS_BUSY		0x01
-#define AT26_STATUS_WRITE_ENABLE	0x02
-
-struct dataflash_local
-{
-	int spi;			/* SPI chip-select number */
-	unsigned int page_size;		/* number of bytes per page */
-};
-
-
-/* Detected DataFlash devices */
-static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES];
-static int nr_devices = 0;
-
-/* Allocate a single SPI transfer descriptor.  We're assuming that if multiple
-   SPI transfers occur at the same time, spi_access_bus() will serialize them.
-   If this is not valid, then either (i) each dataflash 'priv' structure
-   needs it's own transfer descriptor, (ii) we lock this one, or (iii) use
-   another mechanism.   */
-static struct spi_transfer_list* spi_transfer_desc;
-
-/*
- * Perform a SPI transfer to access the DataFlash device.
- */
-static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len,
-		char* txnext, int txnext_len, char* rxnext, int rxnext_len)
-{
-	struct spi_transfer_list* list = spi_transfer_desc;
-
-	list->tx[0] = tx;	list->txlen[0] = tx_len;
-	list->rx[0] = rx;	list->rxlen[0] = rx_len;
-
-	list->tx[1] = txnext; 	list->txlen[1] = txnext_len;
-	list->rx[1] = rxnext;	list->rxlen[1] = rxnext_len;
-
-	list->nr_transfers = nr;
-	/* Note: spi_transfer() always returns 0, there are no error checks */
-	return spi_transfer(list);
-}
-
-/*
- * Return the status of the DataFlash device.
- */
-static unsigned char at91_dataflash26_status(void)
-{
-	unsigned char command[2];
-
-	command[0] = AT26_OP_READ_STATUS;
-	command[1] = 0;
-
-	do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
-
-	return command[1];
-}
-
-/*
- * Poll the DataFlash device until it is READY.
- */
-static unsigned char at91_dataflash26_waitready(void)
-{
-	unsigned char status;
-
-	while (1) {
-		status = at91_dataflash26_status();
-		if (!(status & AT26_STATUS_BUSY))
-			return status;
-	}
-}
-
-/*
- * Enable/disable write access
- */
- static void at91_dataflash26_write_enable(int enable)
-{
-	unsigned char cmd[2];
-
-	DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable);
-
-	if (enable)
-		cmd[0] = AT26_OP_WRITE_ENABLE;
-	else
-		cmd[0] = AT26_OP_WRITE_DISABLE;
-	cmd[1] = 0;
-
-	do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
-}
-
-/*
- * Protect/unprotect sector
- */
- static void at91_dataflash26_sector_protect(loff_t addr, int protect)
-{
-	unsigned char cmd[4];
-
-	DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n",
-	       addr, protect);
-
-	if (protect)
-		cmd[0] = AT26_OP_SECTOR_PROTECT;
-	else
-		cmd[0] = AT26_OP_SECTOR_UNPROTECT;
-	cmd[1] = (addr & 0x00FF0000) >> 16;
-	cmd[2] = (addr & 0x0000FF00) >> 8;
-	cmd[3] = (addr & 0x000000FF);
-
-	do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
-}
-
-/*
- * Erase blocks of flash.
- */
-static int at91_dataflash26_erase(struct mtd_info *mtd,
-				  struct erase_info *instr)
-{
-	struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
-	unsigned char cmd[4];
-
-	DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n",
-	       instr->addr, instr->len);
-
-	/* Sanity checks */
-	if (priv->page_size != 4096)
-		return -EINVAL; /* Can't handle other sizes at the moment */
-
-	if (   ((instr->len % mtd->erasesize) != 0)
-	    || ((instr->len % priv->page_size) != 0)
-	    || ((instr->addr % priv->page_size) != 0)
-	    || ((instr->addr + instr->len) > mtd->size))
-		return -EINVAL;
-
-	spi_access_bus(priv->spi);
-
-	while (instr->len > 0) {
-		at91_dataflash26_write_enable(1);
-		at91_dataflash26_sector_protect(instr->addr, 0);
-		at91_dataflash26_write_enable(1);
-		cmd[0] = AT26_OP_ERASE_PAGE_4K;
-		cmd[1] = (instr->addr & 0x00FF0000) >> 16;
-		cmd[2] = (instr->addr & 0x0000FF00) >> 8;
-		cmd[3] = (instr->addr & 0x000000FF);
-
-		DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x"
-					"0x%02x\n",
-			cmd[0], cmd[1], cmd[2], cmd[3]);
-
-		do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
-		at91_dataflash26_waitready();
-
-		instr->addr += priv->page_size;	 /* next page */
-		instr->len -= priv->page_size;
-	}
-
-	at91_dataflash26_write_enable(0);
-	spi_release_bus(priv->spi);
-
-	/* Inform MTD subsystem that erase is complete */
-	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
-
-	return 0;
-}
-
-/*
- * Read from the DataFlash device.
- *   from   : Start offset in flash device
- *   len    : Number of bytes to read
- *   retlen : Number of bytes actually read
- *   buf    : Buffer that will receive data
- */
-static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len,
-				 size_t *retlen, u_char *buf)
-{
-	struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
-	unsigned char cmd[5];
-
-	DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n",
-	      from, from+len);
-
-	*retlen = 0;
-
-	/* Sanity checks */
-	if (!len)
-		return 0;
-	if (from + len > mtd->size)
-		return -EINVAL;
-
-	cmd[0] = AT26_OP_READ_ARRAY_FAST;
-	cmd[1] = (from & 0x00FF0000) >> 16;
-	cmd[2] = (from & 0x0000FF00) >> 8;
-	cmd[3] = (from & 0x000000FF);
-	/* cmd[4] is a "Don't care" byte  */
-
-	DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n",
-	       cmd[0], cmd[1], cmd[2], cmd[3]);
-
-	spi_access_bus(priv->spi);
-	do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len);
-	spi_release_bus(priv->spi);
-
-	*retlen = len;
-	return 0;
-}
-
-/*
- * Write to the DataFlash device.
- *   to     : Start offset in flash device
- *   len    : Number of bytes to write
- *   retlen : Number of bytes actually written
- *   buf    : Buffer containing the data
- */
-static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len,
-				  size_t *retlen, const u_char *buf)
-{
-	struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
-	unsigned int addr, buf_index = 0;
-	int ret = -EIO, sector, last_sector;
-	unsigned char status, cmd[5];
-
-	DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len);
-
-	*retlen = 0;
-
-	/* Sanity checks */
-	if (!len)
-		return 0;
-	if (to + len > mtd->size)
-		return -EINVAL;
-
-	spi_access_bus(priv->spi);
-
-	addr = to;
-	last_sector = -1;
-
-	while (buf_index < len) {
-		sector = addr / priv->page_size;
-		/* Write first byte if a new sector begins */
-		if (sector != last_sector) {
-			at91_dataflash26_write_enable(1);
-			at91_dataflash26_sector_protect(addr, 0);
-			at91_dataflash26_write_enable(1);
-
-			/* Program first byte of a new sector */
-			cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
-			cmd[1] = (addr & 0x00FF0000) >> 16;
-			cmd[2] = (addr & 0x0000FF00) >> 8;
-			cmd[3] = (addr & 0x000000FF);
-			cmd[4] = buf[buf_index++];
-			do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
-			status = at91_dataflash26_waitready();
-			addr++;
-			/* On write errors, the chip resets the write enable
-			   flag. This also happens after the last byte of a
-			   sector is successfully programmed. */
-			if (   ( !(status & AT26_STATUS_WRITE_ENABLE))
-			    && ((addr % priv->page_size) != 0) ) {
-				DEBUG(MTD_DEBUG_LEVEL1,
-					"write error1: addr=0x%06x, "
-					"status=0x%02x\n", addr, status);
-				goto write_err;
-			}
-			(*retlen)++;
-			last_sector = sector;
-		}
-
-		/* Write subsequent bytes in the same sector */
-		cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
-		cmd[1] = buf[buf_index++];
-		do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
-		status = at91_dataflash26_waitready();
-		addr++;
-
-		if (   ( !(status & AT26_STATUS_WRITE_ENABLE))
-		    && ((addr % priv->page_size) != 0) ) {
-			DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, "
-				"status=0x%02x\n", addr, status);
-			goto write_err;
-		}
-
-		(*retlen)++;
-	}
-
-	ret = 0;
-	at91_dataflash26_write_enable(0);
-write_err:
-	spi_release_bus(priv->spi);
-	return ret;
-}
-
-/*
- * Initialize and register DataFlash device with MTD subsystem.
- */
-static int __init add_dataflash(int channel, char *name, int nr_pages,
-				int pagesize)
-{
-	struct mtd_info *device;
-	struct dataflash_local *priv;
-
-	if (nr_devices >= DATAFLASH_MAX_DEVICES) {
-		printk(KERN_ERR "at91_dataflash26: Too many devices "
-				"detected\n");
-		return 0;
-	}
-
-	device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8,
-			 GFP_KERNEL);
-	if (!device)
-		return -ENOMEM;
-
-	device->name = (char *)&device[1];
-	sprintf(device->name, "%s.spi%d", name, channel);
-	device->size = nr_pages * pagesize;
-	device->erasesize = pagesize;
-	device->owner = THIS_MODULE;
-	device->type = MTD_DATAFLASH;
-	device->flags = MTD_CAP_NORFLASH;
-	device->erase = at91_dataflash26_erase;
-	device->read = at91_dataflash26_read;
-	device->write = at91_dataflash26_write;
-
-	priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local),
-		GFP_KERNEL);
-	if (!priv) {
-		kfree(device);
-		return -ENOMEM;
-	}
-
-	priv->spi = channel;
-	priv->page_size = pagesize;
-	device->priv = priv;
-
-	mtd_devices[nr_devices] = device;
-	nr_devices++;
-	printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n",
-	       name, channel, device->size);
-
-	return add_mtd_device(device);
-}
-
-/*
- * Detect and initialize DataFlash device connected to specified SPI channel.
- *
- */
-
-struct dataflash26_types {
-	unsigned char id0;
-	unsigned char id1;
-	char *name;
-	int pagesize;
-	int nr_pages;
-};
-
-struct dataflash26_types df26_types[] = {
-	{
-		.id0 = 0x04,
-		.id1 = 0x00,
-		.name = "AT26F004",
-		.pagesize = 4096,
-		.nr_pages = 128,
-	},
-	{
-		.id0 = 0x45,
-		.id1 = 0x01,
-		.name = "AT26DF081A", /* Not tested ! */
-		.pagesize = 4096,
-		.nr_pages = 256,
-	},
-};
-
-static int __init at91_dataflash26_detect(int channel)
-{
-	unsigned char status, cmd[5];
-	int i;
-
-	spi_access_bus(channel);
-	status = at91_dataflash26_status();
-
-	if (status == 0 || status == 0xff) {
-		printk(KERN_ERR "at91_dataflash26_detect: status error %d\n",
-			status);
-		spi_release_bus(channel);
-		return -ENODEV;
-	}
-
-	cmd[0] = AT26_OP_READ_DEV_ID;
-	do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
-	spi_release_bus(channel);
-
-	if (cmd[1] != MANUFACTURER_ID_ATMEL)
-		return -ENODEV;
-
-	for (i = 0; i < ARRAY_SIZE(df26_types); i++) {
-		if (   cmd[2] == df26_types[i].id0
-		    && cmd[3] == df26_types[i].id1)
-			return add_dataflash(channel,
-						df26_types[i].name,
-						df26_types[i].nr_pages,
-						df26_types[i].pagesize);
-	}
-
-	printk(KERN_ERR "at91_dataflash26_detect: Unsupported device "
-			"(0x%02x/0x%02x)\n", cmd[2], cmd[3]);
-	return -ENODEV;
-}
-
-static int __init at91_dataflash26_init(void)
-{
-	spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list),
-				    GFP_KERNEL);
-	if (!spi_transfer_desc)
-		return -ENOMEM;
-
-	/* DataFlash (SPI chip select 0) */
-	at91_dataflash26_detect(0);
-
-#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD
-	/* DataFlash card (SPI chip select 3) */
-	at91_dataflash26_detect(3);
-#endif
-	return 0;
-}
-
-static void __exit at91_dataflash26_exit(void)
-{
-	int i;
-
-	for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) {
-		if (mtd_devices[i]) {
-			del_mtd_device(mtd_devices[i]);
-			kfree(mtd_devices[i]->priv);
-			kfree(mtd_devices[i]);
-		}
-	}
-	nr_devices = 0;
-	kfree(spi_transfer_desc);
-}
-
-module_init(at91_dataflash26_init);
-module_exit(at91_dataflash26_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Hans J. Koch");
-MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200");
diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c
index 54aa759..d8cc94e 100644
--- a/drivers/mtd/devices/docprobe.c
+++ b/drivers/mtd/devices/docprobe.c
@@ -81,9 +81,7 @@
 #endif /*  CONFIG_MTD_DOCPROBE_HIGH */
 #elif defined(__PPC__)
 	0xe4000000,
-#elif defined(CONFIG_MOMENCO_OCELOT_G)
-        0xff000000,
-##else
+#else
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #endif
 	0xffffffff };
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 78c2511..98df5bc 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -1,5 +1,5 @@
 /*
- * MTD SPI driver for ST M25Pxx flash chips
+ * MTD SPI driver for ST M25Pxx (and similar) serial flash chips
  *
  * Author: Mike Lavender, mike@steroidmicros.com
  *
@@ -19,33 +19,32 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
-#include <linux/interrupt.h>
+#include <linux/mutex.h>
+
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
-#include <asm/semaphore.h>
-
-
-/* NOTE: AT 25F and SST 25LF series are very similar,
- * but commands for sector erase and chip id differ...
- */
 
 #define FLASH_PAGESIZE		256
 
 /* Flash opcodes. */
-#define	OPCODE_WREN		6	/* Write enable */
-#define	OPCODE_RDSR		5	/* Read status register */
-#define	OPCODE_READ		3	/* Read data bytes */
-#define	OPCODE_PP		2	/* Page program */
-#define	OPCODE_SE		0xd8	/* Sector erase */
-#define	OPCODE_RES		0xab	/* Read Electronic Signature */
+#define	OPCODE_WREN		0x06	/* Write enable */
+#define	OPCODE_RDSR		0x05	/* Read status register */
+#define	OPCODE_READ		0x03	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
+#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
+#define	OPCODE_BE_4K 		0x20	/* Erase 4KiB block */
+#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
+#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
 #define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
 
 /* Status Register bits. */
 #define	SR_WIP			1	/* Write in progress */
 #define	SR_WEL			2	/* Write enable latch */
+/* meaning of other SR_* bits may differ between vendors */
 #define	SR_BP0			4	/* Block protect 0 */
 #define	SR_BP1			8	/* Block protect 1 */
 #define	SR_BP2			0x10	/* Block protect 2 */
@@ -65,9 +64,10 @@
 
 struct m25p {
 	struct spi_device	*spi;
-	struct semaphore	lock;
+	struct mutex		lock;
 	struct mtd_info		mtd;
-	unsigned		partitioned;
+	unsigned		partitioned:1;
+	u8			erase_opcode;
 	u8			command[4];
 };
 
@@ -150,8 +150,9 @@
  */
 static int erase_sector(struct m25p *flash, u32 offset)
 {
-	DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id,
-			__FUNCTION__, offset);
+	DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n",
+			flash->spi->dev.bus_id, __FUNCTION__,
+			flash->mtd.erasesize / 1024, offset);
 
 	/* Wait until finished previous write command. */
 	if (wait_till_ready(flash))
@@ -161,7 +162,7 @@
 	write_enable(flash);
 
 	/* Set up command buffer. */
-	flash->command[0] = OPCODE_SE;
+	flash->command[0] = flash->erase_opcode;
 	flash->command[1] = offset >> 16;
 	flash->command[2] = offset >> 8;
 	flash->command[3] = offset;
@@ -201,13 +202,17 @@
 	addr = instr->addr;
 	len = instr->len;
 
-  	down(&flash->lock);
+	mutex_lock(&flash->lock);
+
+	/* REVISIT in some cases we could speed up erasing large regions
+	 * by using OPCODE_SE instead of OPCODE_BE_4K
+	 */
 
 	/* now erase those sectors */
 	while (len) {
 		if (erase_sector(flash, addr)) {
 			instr->state = MTD_ERASE_FAILED;
-			up(&flash->lock);
+			mutex_unlock(&flash->lock);
 			return -EIO;
 		}
 
@@ -215,7 +220,7 @@
 		len -= mtd->erasesize;
 	}
 
-  	up(&flash->lock);
+	mutex_unlock(&flash->lock);
 
 	instr->state = MTD_ERASE_DONE;
 	mtd_erase_callback(instr);
@@ -260,16 +265,19 @@
 	if (retlen)
 		*retlen = 0;
 
-	down(&flash->lock);
+	mutex_lock(&flash->lock);
 
 	/* Wait till previous write/erase is done. */
 	if (wait_till_ready(flash)) {
 		/* REVISIT status return?? */
-		up(&flash->lock);
+		mutex_unlock(&flash->lock);
 		return 1;
 	}
 
-	/* NOTE:  OPCODE_FAST_READ (if available) is faster... */
+	/* FIXME switch to OPCODE_FAST_READ.  It's required for higher
+	 * clocks; and at this writing, every chip this driver handles
+	 * supports that opcode.
+	 */
 
 	/* Set up the write data buffer. */
 	flash->command[0] = OPCODE_READ;
@@ -281,7 +289,7 @@
 
 	*retlen = m.actual_length - sizeof(flash->command);
 
-  	up(&flash->lock);
+	mutex_unlock(&flash->lock);
 
 	return 0;
 }
@@ -323,7 +331,7 @@
 	t[1].tx_buf = buf;
 	spi_message_add_tail(&t[1], &m);
 
-  	down(&flash->lock);
+	mutex_lock(&flash->lock);
 
 	/* Wait until finished previous write command. */
 	if (wait_till_ready(flash))
@@ -381,10 +389,10 @@
 			if (retlen)
 				*retlen += m.actual_length
 					- sizeof(flash->command);
-	        }
- 	}
+		}
+	}
 
-	up(&flash->lock);
+	mutex_unlock(&flash->lock);
 
 	return 0;
 }
@@ -398,24 +406,118 @@
 
 struct flash_info {
 	char		*name;
-	u8		id;
-	u16		jedec_id;
+
+	/* JEDEC id zero means "no ID" (most older chips); otherwise it has
+	 * a high byte of zero plus three data bytes: the manufacturer id,
+	 * then a two byte device id.
+	 */
+	u32		jedec_id;
+
+	/* The size listed here is what works with OPCODE_SE, which isn't
+	 * necessarily called a "sector" by the vendor.
+	 */
 	unsigned	sector_size;
-	unsigned	n_sectors;
+	u16		n_sectors;
+
+	u16		flags;
+#define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */
 };
 
+
+/* NOTE: double check command sets and memory organization when you add
+ * more flash chips.  This current list focusses on newer chips, which
+ * have been converging on command sets which including JEDEC ID.
+ */
 static struct flash_info __devinitdata m25p_data [] = {
-	/* REVISIT: fill in JEDEC ids, for parts that have them */
-	{ "m25p05", 0x05, 0x2010, 32 * 1024, 2 },
-	{ "m25p10", 0x10, 0x2011, 32 * 1024, 4 },
-	{ "m25p20", 0x11, 0x2012, 64 * 1024, 4 },
-	{ "m25p40", 0x12, 0x2013, 64 * 1024, 8 },
-	{ "m25p80", 0x13, 0x0000, 64 * 1024, 16 },
-	{ "m25p16", 0x14, 0x2015, 64 * 1024, 32 },
-	{ "m25p32", 0x15, 0x2016, 64 * 1024, 64 },
-	{ "m25p64", 0x16, 0x2017, 64 * 1024, 128 },
+
+	/* Atmel -- some are (confusingly) marketed as "DataFlash" */
+	{ "at25fs010",  0x1f6601, 32 * 1024, 4, SECT_4K, },
+	{ "at25fs040",  0x1f6604, 64 * 1024, 8, SECT_4K, },
+
+	{ "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, },
+
+	{ "at26f004",   0x1f0400, 64 * 1024, 8, SECT_4K, },
+	{ "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, },
+	{ "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, },
+	{ "at26df321",  0x1f4701, 64 * 1024, 64, SECT_4K, },
+
+	/* Spansion -- single (large) sector size only, at least
+	 * for the chips listed here (without boot sectors).
+	 */
+	{ "s25sl004a", 0x010212, 64 * 1024, 8, },
+	{ "s25sl008a", 0x010213, 64 * 1024, 16, },
+	{ "s25sl016a", 0x010214, 64 * 1024, 32, },
+	{ "s25sl032a", 0x010215, 64 * 1024, 64, },
+	{ "s25sl064a", 0x010216, 64 * 1024, 128, },
+
+	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
+	{ "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, },
+	{ "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, },
+	{ "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, },
+	{ "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, },
+
+	/* ST Microelectronics -- newer production may have feature updates */
+	{ "m25p05",  0x202010,  32 * 1024, 2, },
+	{ "m25p10",  0x202011,  32 * 1024, 4, },
+	{ "m25p20",  0x202012,  64 * 1024, 4, },
+	{ "m25p40",  0x202013,  64 * 1024, 8, },
+	{ "m25p80",         0,  64 * 1024, 16, },
+	{ "m25p16",  0x202015,  64 * 1024, 32, },
+	{ "m25p32",  0x202016,  64 * 1024, 64, },
+	{ "m25p64",  0x202017,  64 * 1024, 128, },
+	{ "m25p128", 0x202018, 256 * 1024, 64, },
+
+	{ "m45pe80", 0x204014,  64 * 1024, 16, },
+	{ "m45pe16", 0x204015,  64 * 1024, 32, },
+
+	{ "m25pe80", 0x208014,  64 * 1024, 16, },
+	{ "m25pe16", 0x208015,  64 * 1024, 32, SECT_4K, },
+
+	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+	{ "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, },
+	{ "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, },
+	{ "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, },
+	{ "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, },
+	{ "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, },
+	{ "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, },
+	{ "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, },
 };
 
+static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+{
+	int			tmp;
+	u8			code = OPCODE_RDID;
+	u8			id[3];
+	u32			jedec;
+	struct flash_info	*info;
+
+	/* JEDEC also defines an optional "extended device information"
+	 * string for after vendor-specific data, after the three bytes
+	 * we use here.  Supporting some chips might require using it.
+	 */
+	tmp = spi_write_then_read(spi, &code, 1, id, 3);
+	if (tmp < 0) {
+		DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
+			spi->dev.bus_id, tmp);
+		return NULL;
+	}
+	jedec = id[0];
+	jedec = jedec << 8;
+	jedec |= id[1];
+	jedec = jedec << 8;
+	jedec |= id[2];
+
+	for (tmp = 0, info = m25p_data;
+			tmp < ARRAY_SIZE(m25p_data);
+			tmp++, info++) {
+		if (info->jedec_id == jedec)
+			return info;
+	}
+	dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
+	return NULL;
+}
+
+
 /*
  * board specific setup should have ensured the SPI clock used here
  * matches what the READ command supports, at least until this driver
@@ -429,37 +531,51 @@
 	unsigned			i;
 
 	/* Platform data helps sort out which chip type we have, as
-	 * well as how this board partitions it.
+	 * well as how this board partitions it.  If we don't have
+	 * a chip ID, try the JEDEC id commands; they'll work for most
+	 * newer chips, even if we don't recognize the particular chip.
 	 */
 	data = spi->dev.platform_data;
-	if (!data || !data->type) {
-		/* FIXME some chips can identify themselves with RES
-		 * or JEDEC get-id commands.  Try them ...
-		 */
-		DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n",
-				spi->dev.bus_id);
-		return -ENODEV;
-	}
+	if (data && data->type) {
+		for (i = 0, info = m25p_data;
+				i < ARRAY_SIZE(m25p_data);
+				i++, info++) {
+			if (strcmp(data->type, info->name) == 0)
+				break;
+		}
 
-	for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) {
-		if (strcmp(data->type, info->name) == 0)
-			break;
-	}
-	if (i == ARRAY_SIZE(m25p_data)) {
-		DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n",
-				spi->dev.bus_id, data->type);
+		/* unrecognized chip? */
+		if (i == ARRAY_SIZE(m25p_data)) {
+			DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
+					spi->dev.bus_id, data->type);
+			info = NULL;
+
+		/* recognized; is that chip really what's there? */
+		} else if (info->jedec_id) {
+			struct flash_info	*chip = jedec_probe(spi);
+
+			if (!chip || chip != info) {
+				dev_warn(&spi->dev, "found %s, expected %s\n",
+						chip ? chip->name : "UNKNOWN",
+						info->name);
+				info = NULL;
+			}
+		}
+	} else
+		info = jedec_probe(spi);
+
+	if (!info)
 		return -ENODEV;
-	}
 
 	flash = kzalloc(sizeof *flash, GFP_KERNEL);
 	if (!flash)
 		return -ENOMEM;
 
 	flash->spi = spi;
-	init_MUTEX(&flash->lock);
+	mutex_init(&flash->lock);
 	dev_set_drvdata(&spi->dev, flash);
 
-	if (data->name)
+	if (data && data->name)
 		flash->mtd.name = data->name;
 	else
 		flash->mtd.name = spi->dev.bus_id;
@@ -468,17 +584,25 @@
 	flash->mtd.writesize = 1;
 	flash->mtd.flags = MTD_CAP_NORFLASH;
 	flash->mtd.size = info->sector_size * info->n_sectors;
-	flash->mtd.erasesize = info->sector_size;
 	flash->mtd.erase = m25p80_erase;
 	flash->mtd.read = m25p80_read;
 	flash->mtd.write = m25p80_write;
 
+	/* prefer "small sector" erase if possible */
+	if (info->flags & SECT_4K) {
+		flash->erase_opcode = OPCODE_BE_4K;
+		flash->mtd.erasesize = 4096;
+	} else {
+		flash->erase_opcode = OPCODE_SE;
+		flash->mtd.erasesize = info->sector_size;
+	}
+
 	dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name,
 			flash->mtd.size / 1024);
 
 	DEBUG(MTD_DEBUG_LEVEL2,
-		"mtd .name = %s, .size = 0x%.8x (%uM) "
-			".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n",
+		"mtd .name = %s, .size = 0x%.8x (%uMiB) "
+			".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
 		flash->mtd.name,
 		flash->mtd.size, flash->mtd.size / (1024*1024),
 		flash->mtd.erasesize, flash->mtd.erasesize / 1024,
@@ -488,7 +612,7 @@
 		for (i = 0; i < flash->mtd.numeraseregions; i++)
 			DEBUG(MTD_DEBUG_LEVEL2,
 				"mtd.eraseregions[%d] = { .offset = 0x%.8x, "
-				".erasesize = 0x%.8x (%uK), "
+				".erasesize = 0x%.8x (%uKiB), "
 				".numblocks = %d }\n",
 				i, flash->mtd.eraseregions[i].offset,
 				flash->mtd.eraseregions[i].erasesize,
@@ -516,14 +640,14 @@
 		}
 
 		if (nr_parts > 0) {
-			for (i = 0; i < data->nr_parts; i++) {
+			for (i = 0; i < nr_parts; i++) {
 				DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
 					"{.name = %s, .offset = 0x%.8x, "
-						".size = 0x%.8x (%uK) }\n",
-					i, data->parts[i].name,
-					data->parts[i].offset,
-					data->parts[i].size,
-					data->parts[i].size / 1024);
+						".size = 0x%.8x (%uKiB) }\n",
+					i, parts[i].name,
+					parts[i].offset,
+					parts[i].size,
+					parts[i].size / 1024);
 			}
 			flash->partitioned = 1;
 			return add_mtd_partitions(&flash->mtd, parts, nr_parts);
@@ -560,6 +684,11 @@
 	},
 	.probe	= m25p_probe,
 	.remove	= __devexit_p(m25p_remove),
+
+	/* REVISIT: many of these chips have deep power-down modes, which
+	 * should clearly be entered on suspend() to minimize power use.
+	 * And also when they're otherwise idle...
+	 */
 };
 
 
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index a987e91..a5ed6d2 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/mutex.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
@@ -89,7 +90,7 @@
 	unsigned short		page_offset;	/* offset in flash address */
 	unsigned int		page_size;	/* of bytes per page */
 
-	struct semaphore	lock;
+	struct mutex		lock;
 	struct spi_device	*spi;
 
 	struct mtd_info		mtd;
@@ -167,7 +168,7 @@
 	x.len = 4;
 	spi_message_add_tail(&x, &msg);
 
-	down(&priv->lock);
+	mutex_lock(&priv->lock);
 	while (instr->len > 0) {
 		unsigned int	pageaddr;
 		int		status;
@@ -210,7 +211,7 @@
 			instr->len -= priv->page_size;
 		}
 	}
-	up(&priv->lock);
+	mutex_unlock(&priv->lock);
 
 	/* Inform MTD subsystem that erase is complete */
 	instr->state = MTD_ERASE_DONE;
@@ -266,7 +267,7 @@
 	x[1].len = len;
 	spi_message_add_tail(&x[1], &msg);
 
-	down(&priv->lock);
+	mutex_lock(&priv->lock);
 
 	/* Continuous read, max clock = f(car) which may be less than
 	 * the peak rate available.  Some chips support commands with
@@ -279,7 +280,7 @@
 	/* plus 4 "don't care" bytes */
 
 	status = spi_sync(priv->spi, &msg);
-	up(&priv->lock);
+	mutex_unlock(&priv->lock);
 
 	if (status >= 0) {
 		*retlen = msg.actual_length - 8;
@@ -336,7 +337,7 @@
 	else
 		writelen = len;
 
-	down(&priv->lock);
+	mutex_lock(&priv->lock);
 	while (remaining > 0) {
 		DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n",
 			pageaddr, offset, writelen);
@@ -441,7 +442,7 @@
 		else
 			writelen = remaining;
 	}
-	up(&priv->lock);
+	mutex_unlock(&priv->lock);
 
 	return status;
 }
@@ -463,7 +464,7 @@
 	if (!priv)
 		return -ENOMEM;
 
-	init_MUTEX(&priv->lock);
+	mutex_init(&priv->lock);
 	priv->spi = spi;
 	priv->page_size = pagesize;
 	priv->page_offset = pageoffset;
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index e8f686f..7060a08 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -30,8 +30,8 @@
  *
  * Notes:
  *	Due to what I assume is more buggy SROM, the 64M PMC551 I
- *	have available claims that all 4 of it's DRAM banks have 64M
- *	of ram configured (making a grand total of 256M onboard).
+ *	have available claims that all 4 of its DRAM banks have 64MiB
+ *	of ram configured (making a grand total of 256MiB onboard).
  *	This is slightly annoying since the BAR0 size reflects the
  *	aperture size, not the dram size, and the V370PDC supplies no
  *	other method for memory size discovery.  This problem is
@@ -70,7 +70,7 @@
  *	 made the memory unusable, added a fix to code to touch up
  *	 the DRAM some.
  *
- * Bugs/FIXME's:
+ * Bugs/FIXMEs:
  *	* MUST fix the init function to not spin on a register
  *	waiting for it to set .. this does not safely handle busted
  *	devices that never reset the register correctly which will
@@ -562,10 +562,10 @@
 	/*
 	 * Some screen fun
 	 */
-	printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at "
+	printk(KERN_DEBUG "pmc551: %d%sB (0x%x) of %sprefetchable memory at "
 		"0x%llx\n", (size < 1024) ? size : (size < 1048576) ?
 		size >> 10 : size >> 20,
-		(size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size,
+		(size < 1024) ? "" : (size < 1048576) ? "Ki" : "Mi", size,
 		((dcmd & (0x1 << 3)) == 0) ? "non-" : "",
 		(unsigned long long)pci_resource_start(dev, 0));
 
@@ -649,14 +649,10 @@
  * Stuff these outside the ifdef so as to not bust compiled in driver support
  */
 static int msize = 0;
-#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE)
-static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE;
-#else
 static int asize = 0;
-#endif
 
 module_param(msize, int, 0);
-MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]");
+MODULE_PARM_DESC(msize, "memory size in MiB [1 - 1024]");
 module_param(asize, int, 0);
 MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]");
 
@@ -799,8 +795,7 @@
 		mtd->owner = THIS_MODULE;
 
 		if (add_mtd_device(mtd)) {
-			printk(KERN_NOTICE "pmc551: Failed to register new "
-				"device\n");
+			printk(KERN_NOTICE "pmc551: Failed to register new device\n");
 			pci_iounmap(PCI_Device, priv->start);
 			kfree(mtd->priv);
 			kfree(mtd);
@@ -811,13 +806,13 @@
 		pci_dev_get(PCI_Device);
 
 		printk(KERN_NOTICE "Registered pmc551 memory device.\n");
-		printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n",
+		printk(KERN_NOTICE "Mapped %dMiB of memory from 0x%p to 0x%p\n",
 			priv->asize >> 20,
 			priv->start, priv->start + priv->asize);
-		printk(KERN_NOTICE "Total memory is %d%c\n",
+		printk(KERN_NOTICE "Total memory is %d%sB\n",
 			(length < 1024) ? length :
 			(length < 1048576) ? length >> 10 : length >> 20,
-			(length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M');
+			(length < 1024) ? "" : (length < 1048576) ? "Ki" : "Mi");
 		priv->nextpmc551 = pmc551list;
 		pmc551list = mtd;
 		found++;
@@ -850,7 +845,7 @@
 		pmc551list = priv->nextpmc551;
 
 		if (priv->start) {
-			printk(KERN_DEBUG "pmc551: unmapping %dM starting at "
+			printk(KERN_DEBUG "pmc551: unmapping %dMiB starting at "
 				"0x%p\n", priv->asize >> 20, priv->start);
 			pci_iounmap(priv->dev, priv->start);
 		}
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c
index ecac0e4..b8917be 100644
--- a/drivers/mtd/inftlmount.c
+++ b/drivers/mtd/inftlmount.c
@@ -580,14 +580,13 @@
 	logical_block = block = BLOCK_NIL;
 
 	/* Temporary buffer to store ANAC numbers. */
-	ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL);
+	ANACtable = kcalloc(s->nb_blocks, sizeof(u8), GFP_KERNEL);
 	if (!ANACtable) {
 		printk(KERN_WARNING "INFTL: allocation of ANACtable "
 				"failed (%zd bytes)\n",
 				s->nb_blocks * sizeof(u8));
 		return -ENOMEM;
 	}
-	memset(ANACtable, 0, s->nb_blocks);
 
 	/*
 	 * First pass is to explore each physical unit, and construct the
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 6cd132c..2a2a125 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -163,20 +163,12 @@
 	  More info at
 	  <http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>.
 
-config MTD_LUBBOCK
-	tristate "CFI Flash device mapped on Intel Lubbock XScale eval board"
-	depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS
-	help
-	  This provides a driver for the on-board flash of the Intel
-	  'Lubbock' XScale evaluation board.
-
-config MTD_MAINSTONE
-	tristate "CFI Flash device mapped on Intel Mainstone XScale eval board"
-	depends on MACH_MAINSTONE && MTD_CFI_INTELEXT
+config MTD_PXA2XX
+	tristate "CFI Flash device mapped on Intel XScale PXA2xx based boards"
+	depends on (PXA25x || PXA27x) && MTD_CFI_INTELEXT
 	select MTD_PARTITIONS
 	help
-	  This provides a driver for the on-board flash of the Intel
-	  'Mainstone PXA27x evaluation board.
+	  This provides a driver for the NOR flash attached to a PXA2xx chip.
 
 config MTD_OCTAGON
 	tristate "JEDEC Flash device mapped on Octagon 5066 SBC"
@@ -354,7 +346,7 @@
 
 config MTD_WALNUT
 	tristate "Flash device mapped on IBM 405GP Walnut"
-	depends on MTD_JEDECPROBE && WALNUT
+	depends on MTD_JEDECPROBE && WALNUT && !PPC_MERGE
 	help
 	  This enables access routines for the flash chips on the IBM 405GP
 	  Walnut board. If you have one of these boards and would like to
@@ -370,7 +362,7 @@
 
 config MTD_OCOTEA
 	tristate "Flash devices mapped on IBM 440GX Ocotea"
-	depends on MTD_CFI && OCOTEA
+	depends on MTD_CFI && OCOTEA && !PPC_MERGE
 	help
 	  This enables access routines for the flash chips on the IBM 440GX
 	  Ocotea board. If you have one of these boards and would like to
@@ -384,22 +376,6 @@
 	  Redwood board. If you have one of these boards and would like to
 	  use the flash chips on it, say 'Y'.
 
-config MTD_TQM834x
-	tristate "Flash device mapped on TQ Components TQM834x Boards"
-	depends on MTD_CFI && TQM834x
-	help
-	  This enables access routines for the flash chips on the
-	  TQ Components TQM834x boards. If you have one of these boards
-	  and would like to use the flash chips on it, say 'Y'.
-
-config MTD_OCELOT
-	tristate "Momenco Ocelot boot flash device"
-	depends on MOMENCO_OCELOT
-	help
-	  This enables access routines for the boot flash device and for the
-	  NVRAM on the Momenco Ocelot board. If you have one of these boards
-	  and would like access to either of these, say 'Y'.
-
 config MTD_SOLUTIONENGINE
 	tristate "CFI Flash device mapped on Hitachi SolutionEngine"
 	depends on SUPERH && MTD_CFI && MTD_REDBOOT_PARTS
@@ -605,6 +581,13 @@
 	help
 	  This enables access to the flash chip on the Sharp SL Series of PDAs.
 
+config MTD_INTEL_VR_NOR
+	tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
+	depends on PCI
+	help
+	  Map driver for a NOR flash bank located on the Expansion Bus of the
+	  Intel Vermilion Range chipset.
+
 config MTD_PLATRAM
 	tristate "Map driver for platform device RAM (mtd-ram)"
 	select MTD_RAM
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 970b189..316382a 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -20,8 +20,7 @@
 obj-$(CONFIG_MTD_ICHXROM)	+= ichxrom.o
 obj-$(CONFIG_MTD_CK804XROM)	+= ck804xrom.o
 obj-$(CONFIG_MTD_TSUNAMI)	+= tsunami_flash.o
-obj-$(CONFIG_MTD_LUBBOCK)	+= lubbock-flash.o
-obj-$(CONFIG_MTD_MAINSTONE)	+= mainstone-flash.o
+obj-$(CONFIG_MTD_PXA2XX)	+= pxa2xx-flash.o
 obj-$(CONFIG_MTD_MBX860)	+= mbx860.o
 obj-$(CONFIG_MTD_CEIVA)		+= ceiva.o
 obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
@@ -43,7 +42,6 @@
 obj-$(CONFIG_MTD_VMAX)		+= vmax301.o
 obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
 obj-$(CONFIG_MTD_DBOX2)		+= dbox2-flash.o
-obj-$(CONFIG_MTD_OCELOT)	+= ocelot.o
 obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
 obj-$(CONFIG_MTD_PCI)		+= pci.o
 obj-$(CONFIG_MTD_ALCHEMY)       += alchemy-flash.o
@@ -70,4 +68,4 @@
 obj-$(CONFIG_MTD_PLATRAM)	+= plat-ram.o
 obj-$(CONFIG_MTD_OMAP_NOR)	+= omap_nor.o
 obj-$(CONFIG_MTD_MTX1)		+= mtx-1_flash.o
-obj-$(CONFIG_MTD_TQM834x)	+= tqm834x.o
+obj-$(CONFIG_MTD_INTEL_VR_NOR)	+= intel_vr_nor.o
diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c
index 84fbe0e..82811bc 100644
--- a/drivers/mtd/maps/alchemy-flash.c
+++ b/drivers/mtd/maps/alchemy-flash.c
@@ -75,13 +75,6 @@
 #define BOARD_FLASH_WIDTH 2 /* 16-bits */
 #endif
 
-#ifdef CONFIG_MIPS_HYDROGEN3
-#define BOARD_MAP_NAME "Hydrogen3 Flash"
-#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#define USE_LOCAL_ACCESSORS /* why? */
-#endif
-
 #ifdef CONFIG_MIPS_BOSPORUS
 #define BOARD_MAP_NAME "Bosporus Flash"
 #define BOARD_FLASH_SIZE 0x01000000 /* 16MB */
@@ -130,13 +123,6 @@
 
 	window_addr = 0x20000000 - BOARD_FLASH_SIZE;
 	window_size = BOARD_FLASH_SIZE;
-#ifdef CONFIG_MIPS_MIRAGE_WHY
-	/* Boot ROM flash bank only; no user bank */
-	window_addr = 0x1C000000;
-	window_size = 0x04000000;
-	/* USERFS from 0x1C00 0000 to 0x1FC00000 */
-	alchemy_partitions[0].size = 0x03C00000;
-#endif
 
 	/*
 	 * Static partition definition selection
diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c
new file mode 100644
index 0000000..1e7814a
--- /dev/null
+++ b/drivers/mtd/maps/intel_vr_nor.c
@@ -0,0 +1,298 @@
+/*
+ * drivers/mtd/maps/intel_vr_nor.c
+ *
+ * An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel
+ * Vermilion Range chipset.
+ *
+ * The Vermilion Range Expansion Bus supports four chip selects, each of which
+ * has 64MiB of address space.  The 2nd BAR of the Expansion Bus PCI Device
+ * is a 256MiB memory region containing the address spaces for all four of the
+ * chip selects, with start addresses hardcoded on 64MiB boundaries.
+ *
+ * This map driver only supports NOR flash on chip select 0.  The buswidth
+ * (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing
+ * and Control Register for Chip Select 0 (EXP_TIMING_CS0).  This driver does
+ * not modify the value in the EXP_TIMING_CS0 register except to enable writing
+ * and disable boot acceleration.  The timing parameters in the register are
+ * assumed to have been properly initialized by the BIOS.  The reset default
+ * timing parameters are maximally conservative (slow), so access to the flash
+ * will be slower than it should be if the BIOS has not initialized the timing
+ * parameters.
+ *
+ * Author: Andy Lowe <alowe@mvista.com>
+ *
+ * 2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/flashchip.h>
+
+#define DRV_NAME "vr_nor"
+
+struct vr_nor_mtd {
+	void __iomem *csr_base;
+	struct map_info map;
+	struct mtd_info *info;
+	int nr_parts;
+	struct pci_dev *dev;
+};
+
+/* Expansion Bus Configuration and Status Registers are in BAR 0 */
+#define EXP_CSR_MBAR 0
+/* Expansion Bus Memory Window is BAR 1 */
+#define EXP_WIN_MBAR 1
+/* Maximum address space for Chip Select 0 is 64MiB */
+#define CS0_SIZE 0x04000000
+/* Chip Select 0 is at offset 0 in the Memory Window */
+#define CS0_START 0x0
+/* Chip Select 0 Timing Register is at offset 0 in CSR */
+#define EXP_TIMING_CS0 0x00
+#define TIMING_CS_EN		(1 << 31)	/* Chip Select Enable */
+#define TIMING_BOOT_ACCEL_DIS	(1 <<  8)	/* Boot Acceleration Disable */
+#define TIMING_WR_EN		(1 <<  1)	/* Write Enable */
+#define TIMING_BYTE_EN		(1 <<  0)	/* 8-bit vs 16-bit bus */
+#define TIMING_MASK		0x3FFF0000
+
+static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p)
+{
+	if (p->nr_parts > 0) {
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+		del_mtd_partitions(p->info);
+#endif
+	} else
+		del_mtd_device(p->info);
+}
+
+static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p)
+{
+	int err = 0;
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+	struct mtd_partition *parts;
+	static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+	/* register the flash bank */
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+	/* partition the flash bank */
+	p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0);
+	if (p->nr_parts > 0)
+		err = add_mtd_partitions(p->info, parts, p->nr_parts);
+#endif
+	if (p->nr_parts <= 0)
+		err = add_mtd_device(p->info);
+
+	return err;
+}
+
+static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
+{
+	map_destroy(p->info);
+}
+
+static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p)
+{
+	static const char *probe_types[] =
+	    { "cfi_probe", "jedec_probe", NULL };
+	const char **type;
+
+	for (type = probe_types; !p->info && *type; type++)
+		p->info = do_map_probe(*type, &p->map);
+	if (!p->info)
+		return -ENODEV;
+
+	p->info->owner = THIS_MODULE;
+
+	return 0;
+}
+
+static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p)
+{
+	unsigned int exp_timing_cs0;
+
+	/* write-protect the flash bank */
+	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
+	exp_timing_cs0 &= ~TIMING_WR_EN;
+	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
+
+	/* unmap the flash window */
+	iounmap(p->map.virt);
+
+	/* unmap the csr window */
+	iounmap(p->csr_base);
+}
+
+/*
+ * Initialize the map_info structure and map the flash.
+ * Returns 0 on success, nonzero otherwise.
+ */
+static int __devinit vr_nor_init_maps(struct vr_nor_mtd *p)
+{
+	unsigned long csr_phys, csr_len;
+	unsigned long win_phys, win_len;
+	unsigned int exp_timing_cs0;
+	int err;
+
+	csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR);
+	csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR);
+	win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR);
+	win_len = pci_resource_len(p->dev, EXP_WIN_MBAR);
+
+	if (!csr_phys || !csr_len || !win_phys || !win_len)
+		return -ENODEV;
+
+	if (win_len < (CS0_START + CS0_SIZE))
+		return -ENXIO;
+
+	p->csr_base = ioremap_nocache(csr_phys, csr_len);
+	if (!p->csr_base)
+		return -ENOMEM;
+
+	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
+	if (!(exp_timing_cs0 & TIMING_CS_EN)) {
+		dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
+		       "is disabled.\n");
+		err = -ENODEV;
+		goto release;
+	}
+	if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) {
+		dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
+		       "is configured for maximally slow access times.\n");
+	}
+	p->map.name = DRV_NAME;
+	p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2;
+	p->map.phys = win_phys + CS0_START;
+	p->map.size = CS0_SIZE;
+	p->map.virt = ioremap_nocache(p->map.phys, p->map.size);
+	if (!p->map.virt) {
+		err = -ENOMEM;
+		goto release;
+	}
+	simple_map_init(&p->map);
+
+	/* Enable writes to flash bank */
+	exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN;
+	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
+
+	return 0;
+
+      release:
+	iounmap(p->csr_base);
+	return err;
+}
+
+static struct pci_device_id vr_nor_pci_ids[] = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},
+	{0,}
+};
+
+static void __devexit vr_nor_pci_remove(struct pci_dev *dev)
+{
+	struct vr_nor_mtd *p = pci_get_drvdata(dev);
+
+	pci_set_drvdata(dev, NULL);
+	vr_nor_destroy_partitions(p);
+	vr_nor_destroy_mtd_setup(p);
+	vr_nor_destroy_maps(p);
+	kfree(p);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+}
+
+static int __devinit
+vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct vr_nor_mtd *p = NULL;
+	unsigned int exp_timing_cs0;
+	int err;
+
+	err = pci_enable_device(dev);
+	if (err)
+		goto out;
+
+	err = pci_request_regions(dev, DRV_NAME);
+	if (err)
+		goto disable_dev;
+
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	err = -ENOMEM;
+	if (!p)
+		goto release;
+
+	p->dev = dev;
+
+	err = vr_nor_init_maps(p);
+	if (err)
+		goto release;
+
+	err = vr_nor_mtd_setup(p);
+	if (err)
+		goto destroy_maps;
+
+	err = vr_nor_init_partitions(p);
+	if (err)
+		goto destroy_mtd_setup;
+
+	pci_set_drvdata(dev, p);
+
+	return 0;
+
+      destroy_mtd_setup:
+	map_destroy(p->info);
+
+      destroy_maps:
+	/* write-protect the flash bank */
+	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
+	exp_timing_cs0 &= ~TIMING_WR_EN;
+	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
+
+	/* unmap the flash window */
+	iounmap(p->map.virt);
+
+	/* unmap the csr window */
+	iounmap(p->csr_base);
+
+      release:
+	kfree(p);
+	pci_release_regions(dev);
+
+      disable_dev:
+	pci_disable_device(dev);
+
+      out:
+	return err;
+}
+
+static struct pci_driver vr_nor_pci_driver = {
+	.name = DRV_NAME,
+	.probe = vr_nor_pci_probe,
+	.remove = __devexit_p(vr_nor_pci_remove),
+	.id_table = vr_nor_pci_ids,
+};
+
+static int __init vr_nor_mtd_init(void)
+{
+	return pci_register_driver(&vr_nor_pci_driver);
+}
+
+static void __exit vr_nor_mtd_exit(void)
+{
+	pci_unregister_driver(&vr_nor_pci_driver);
+}
+
+module_init(vr_nor_mtd_init);
+module_exit(vr_nor_mtd_exit);
+
+MODULE_AUTHOR("Andy Lowe");
+MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids);
diff --git a/drivers/mtd/maps/lubbock-flash.c b/drivers/mtd/maps/lubbock-flash.c
deleted file mode 100644
index e856068..0000000
--- a/drivers/mtd/maps/lubbock-flash.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * $Id: lubbock-flash.c,v 1.21 2005/11/07 11:14:27 gleixner Exp $
- *
- * Map driver for the Lubbock developer platform.
- *
- * Author:	Nicolas Pitre
- * Copyright:	(C) 2001 MontaVista Software Inc.
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/lubbock.h>
-#include <asm/cacheflush.h>
-
-#define ROM_ADDR	0x00000000
-#define FLASH_ADDR	0x04000000
-
-#define WINDOW_SIZE 	64*1024*1024
-
-static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len)
-{
-	flush_ioremap_region(map->phys, map->cached, from, len);
-}
-
-static struct map_info lubbock_maps[2] = { {
-	.size =		WINDOW_SIZE,
-	.phys =		0x00000000,
-	.inval_cache = 	lubbock_map_inval_cache,
-}, {
-	.size =		WINDOW_SIZE,
-	.phys =		0x04000000,
-	.inval_cache = 	lubbock_map_inval_cache,
-} };
-
-static struct mtd_partition lubbock_partitions[] = {
-	{
-		.name =		"Bootloader",
-		.size =		0x00040000,
-		.offset =	0,
-		.mask_flags =	MTD_WRITEABLE  /* force read-only */
-	},{
-		.name =		"Kernel",
-		.size =		0x00100000,
-		.offset =	0x00040000,
-	},{
-		.name =		"Filesystem",
-		.size =		MTDPART_SIZ_FULL,
-		.offset =	0x00140000
-	}
-};
-
-static struct mtd_info *mymtds[2];
-static struct mtd_partition *parsed_parts[2];
-static int nr_parsed_parts[2];
-
-static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-
-static int __init init_lubbock(void)
-{
-	int flashboot = (LUB_CONF_SWITCHES & 1);
-	int ret = 0, i;
-
-	lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth =
-		(BOOT_DEF & 1) ? 2 : 4;
-
-	/* Compensate for the nROMBT switch which swaps the flash banks */
-	printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n",
-	       flashboot?"Flash":"ROM", flashboot);
-
-	lubbock_maps[flashboot^1].name = "Lubbock Application Flash";
-	lubbock_maps[flashboot].name = "Lubbock Boot ROM";
-
-	for (i = 0; i < 2; i++) {
-		lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE);
-		if (!lubbock_maps[i].virt) {
-			printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name);
-			if (!ret)
-				ret = -ENOMEM;
-			continue;
-		}
-		lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE);
-		if (!lubbock_maps[i].cached)
-			printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name);
-		simple_map_init(&lubbock_maps[i]);
-
-		printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n",
-		       lubbock_maps[i].name, lubbock_maps[i].phys,
-		       lubbock_maps[i].bankwidth * 8);
-
-		mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
-
-		if (!mymtds[i]) {
-			iounmap((void *)lubbock_maps[i].virt);
-			if (lubbock_maps[i].cached)
-				iounmap(lubbock_maps[i].cached);
-			if (!ret)
-				ret = -EIO;
-			continue;
-		}
-		mymtds[i]->owner = THIS_MODULE;
-
-		ret = parse_mtd_partitions(mymtds[i], probes,
-					   &parsed_parts[i], 0);
-
-		if (ret > 0)
-			nr_parsed_parts[i] = ret;
-	}
-
-	if (!mymtds[0] && !mymtds[1])
-		return ret;
-
-	for (i = 0; i < 2; i++) {
-		if (!mymtds[i]) {
-			printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name);
-		} else if (nr_parsed_parts[i]) {
-			add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]);
-		} else if (!i) {
-			printk("Using static partitions on %s\n", lubbock_maps[i].name);
-			add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions));
-		} else {
-			printk("Registering %s as whole device\n", lubbock_maps[i].name);
-			add_mtd_device(mymtds[i]);
-		}
-	}
-	return 0;
-}
-
-static void __exit cleanup_lubbock(void)
-{
-	int i;
-	for (i = 0; i < 2; i++) {
-		if (!mymtds[i])
-			continue;
-
-		if (nr_parsed_parts[i] || !i)
-			del_mtd_partitions(mymtds[i]);
-		else
-			del_mtd_device(mymtds[i]);
-
-		map_destroy(mymtds[i]);
-		iounmap((void *)lubbock_maps[i].virt);
-		if (lubbock_maps[i].cached)
-			iounmap(lubbock_maps[i].cached);
-
-		kfree(parsed_parts[i]);
-	}
-}
-
-module_init(init_lubbock);
-module_exit(cleanup_lubbock);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
-MODULE_DESCRIPTION("MTD map driver for Intel Lubbock");
diff --git a/drivers/mtd/maps/mainstone-flash.c b/drivers/mtd/maps/mainstone-flash.c
deleted file mode 100644
index d76487d..0000000
--- a/drivers/mtd/maps/mainstone-flash.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * $Id:  $
- *
- * Map driver for the Mainstone developer platform.
- *
- * Author:	Nicolas Pitre
- * Copyright:	(C) 2001 MontaVista Software Inc.
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/mainstone.h>
-#include <asm/cacheflush.h>
-
-
-#define ROM_ADDR	0x00000000
-#define FLASH_ADDR	0x04000000
-
-#define WINDOW_SIZE 	0x04000000
-
-static void mainstone_map_inval_cache(struct map_info *map, unsigned long from,
-				      ssize_t len)
-{
-	flush_ioremap_region(map->phys, map->cached, from, len);
-}
-
-static struct map_info mainstone_maps[2] = { {
-	.size =		WINDOW_SIZE,
-	.phys =		PXA_CS0_PHYS,
-	.inval_cache = 	mainstone_map_inval_cache,
-}, {
-	.size =		WINDOW_SIZE,
-	.phys =		PXA_CS1_PHYS,
-	.inval_cache = 	mainstone_map_inval_cache,
-} };
-
-static struct mtd_partition mainstone_partitions[] = {
-	{
-		.name =		"Bootloader",
-		.size =		0x00040000,
-		.offset =	0,
-		.mask_flags =	MTD_WRITEABLE  /* force read-only */
-	},{
-		.name =		"Kernel",
-		.size =		0x00400000,
-		.offset =	0x00040000,
-	},{
-		.name =		"Filesystem",
-		.size =		MTDPART_SIZ_FULL,
-		.offset =	0x00440000
-	}
-};
-
-static struct mtd_info *mymtds[2];
-static struct mtd_partition *parsed_parts[2];
-static int nr_parsed_parts[2];
-
-static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-
-static int __init init_mainstone(void)
-{
-	int SW7 = 0;  /* FIXME: get from SCR (Mst doc section 3.2.1.1) */
-	int ret = 0, i;
-
-	mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4;
-	mainstone_maps[1].bankwidth = 4;
-
-	/* Compensate for SW7 which swaps the flash banks */
-	mainstone_maps[SW7].name = "processor flash";
-	mainstone_maps[SW7 ^ 1].name = "main board flash";
-
-	printk(KERN_NOTICE "Mainstone configured to boot from %s\n",
-	       mainstone_maps[0].name);
-
-	for (i = 0; i < 2; i++) {
-		mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys,
-						 WINDOW_SIZE);
-		if (!mainstone_maps[i].virt) {
-			printk(KERN_WARNING "Failed to ioremap %s\n",
-			       mainstone_maps[i].name);
-			if (!ret)
-				ret = -ENOMEM;
-			continue;
-		}
-		mainstone_maps[i].cached =
-			ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE);
-		if (!mainstone_maps[i].cached)
-			printk(KERN_WARNING "Failed to ioremap cached %s\n",
-			       mainstone_maps[i].name);
-		simple_map_init(&mainstone_maps[i]);
-
-		printk(KERN_NOTICE
-		       "Probing %s at physical address 0x%08lx"
-		       " (%d-bit bankwidth)\n",
-		       mainstone_maps[i].name, mainstone_maps[i].phys,
-		       mainstone_maps[i].bankwidth * 8);
-
-		mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]);
-
-		if (!mymtds[i]) {
-			iounmap((void *)mainstone_maps[i].virt);
-			if (mainstone_maps[i].cached)
-				iounmap(mainstone_maps[i].cached);
-			if (!ret)
-				ret = -EIO;
-			continue;
-		}
-		mymtds[i]->owner = THIS_MODULE;
-
-		ret = parse_mtd_partitions(mymtds[i], probes,
-					   &parsed_parts[i], 0);
-
-		if (ret > 0)
-			nr_parsed_parts[i] = ret;
-	}
-
-	if (!mymtds[0] && !mymtds[1])
-		return ret;
-
-	for (i = 0; i < 2; i++) {
-		if (!mymtds[i]) {
-			printk(KERN_WARNING "%s is absent. Skipping\n",
-			       mainstone_maps[i].name);
-		} else if (nr_parsed_parts[i]) {
-			add_mtd_partitions(mymtds[i], parsed_parts[i],
-					   nr_parsed_parts[i]);
-		} else if (!i) {
-			printk("Using static partitions on %s\n",
-			       mainstone_maps[i].name);
-			add_mtd_partitions(mymtds[i], mainstone_partitions,
-					   ARRAY_SIZE(mainstone_partitions));
-		} else {
-			printk("Registering %s as whole device\n",
-			       mainstone_maps[i].name);
-			add_mtd_device(mymtds[i]);
-		}
-	}
-	return 0;
-}
-
-static void __exit cleanup_mainstone(void)
-{
-	int i;
-	for (i = 0; i < 2; i++) {
-		if (!mymtds[i])
-			continue;
-
-		if (nr_parsed_parts[i] || !i)
-			del_mtd_partitions(mymtds[i]);
-		else
-			del_mtd_device(mymtds[i]);
-
-		map_destroy(mymtds[i]);
-		iounmap((void *)mainstone_maps[i].virt);
-		if (mainstone_maps[i].cached)
-			iounmap(mainstone_maps[i].cached);
-		kfree(parsed_parts[i]);
-	}
-}
-
-module_init(init_mainstone);
-module_exit(cleanup_mainstone);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
-MODULE_DESCRIPTION("MTD map driver for Intel Mainstone");
diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c
index 7b96cd02..0c9b305 100644
--- a/drivers/mtd/maps/nettel.c
+++ b/drivers/mtd/maps/nettel.c
@@ -158,68 +158,11 @@
 	nettel_reboot_notifier, NULL, 0
 };
 
-/*
- *	Erase the configuration file system.
- *	Used to support the software reset button.
- */
-static void nettel_erasecallback(struct erase_info *done)
-{
-	wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
-	wake_up(wait_q);
-}
-
-static struct erase_info nettel_erase;
-
-int nettel_eraseconfig(void)
-{
-	struct mtd_info *mtd;
-	DECLARE_WAITQUEUE(wait, current);
-	wait_queue_head_t wait_q;
-	int ret;
-
-	init_waitqueue_head(&wait_q);
-	mtd = get_mtd_device(NULL, 2);
-	if (!IS_ERR(mtd)) {
-		nettel_erase.mtd = mtd;
-		nettel_erase.callback = nettel_erasecallback;
-		nettel_erase.callback = NULL;
-		nettel_erase.addr = 0;
-		nettel_erase.len = mtd->size;
-		nettel_erase.priv = (u_long) &wait_q;
-		nettel_erase.priv = 0;
-
-		set_current_state(TASK_INTERRUPTIBLE);
-		add_wait_queue(&wait_q, &wait);
-
-		ret = mtd->erase(mtd, &nettel_erase);
-		if (ret) {
-			set_current_state(TASK_RUNNING);
-			remove_wait_queue(&wait_q, &wait);
-			put_mtd_device(mtd);
-			return(ret);
-		}
-
-		schedule();  /* Wait for erase to finish. */
-		remove_wait_queue(&wait_q, &wait);
-
-		put_mtd_device(mtd);
-	}
-
-	return(0);
-}
-
-#else
-
-int nettel_eraseconfig(void)
-{
-	return(0);
-}
-
 #endif
 
 /****************************************************************************/
 
-int __init nettel_init(void)
+static int __init nettel_init(void)
 {
 	volatile unsigned long *amdpar;
 	unsigned long amdaddr, maxsize;
@@ -421,10 +364,6 @@
 
 	intel_mtd->owner = THIS_MODULE;
 
-#ifndef CONFIG_BLK_DEV_INITRD
-	ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1);
-#endif
-
 	num_intel_partitions = sizeof(nettel_intel_partitions) /
 		sizeof(nettel_intel_partitions[0]);
 
@@ -477,7 +416,7 @@
 
 /****************************************************************************/
 
-void __exit nettel_cleanup(void)
+static void __exit nettel_cleanup(void)
 {
 #ifdef CONFIG_MTD_CFI_INTELEXT
 	unregister_reboot_notifier(&nettel_notifier_block);
diff --git a/drivers/mtd/maps/ocelot.c b/drivers/mtd/maps/ocelot.c
deleted file mode 100644
index 6977963..0000000
--- a/drivers/mtd/maps/ocelot.c
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * $Id: ocelot.c,v 1.17 2005/11/07 11:14:27 gleixner Exp $
- *
- * Flash on Momenco Ocelot
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#define OCELOT_PLD 0x2c000000
-#define FLASH_WINDOW_ADDR 0x2fc00000
-#define FLASH_WINDOW_SIZE 0x00080000
-#define FLASH_BUSWIDTH 1
-#define NVRAM_WINDOW_ADDR 0x2c800000
-#define NVRAM_WINDOW_SIZE 0x00007FF0
-#define NVRAM_BUSWIDTH 1
-
-static unsigned int cacheflush = 0;
-
-static struct mtd_info *flash_mtd;
-static struct mtd_info *nvram_mtd;
-
-static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
-{
-        struct map_info *map = mtd->priv;
-	size_t done = 0;
-
-	/* If we use memcpy, it does word-wide writes. Even though we told the
-	   GT64120A that it's an 8-bit wide region, word-wide writes don't work.
-	   We end up just writing the first byte of the four to all four bytes.
-	   So we have this loop instead */
-	*retlen = len;
-	while(len) {
-		__raw_writeb(*(unsigned char *) from, map->virt + to);
-		from++;
-		to++;
-		len--;
-	}
-}
-
-static struct mtd_partition *parsed_parts;
-
-struct map_info ocelot_flash_map = {
-	.name = "Ocelot boot flash",
-	.size = FLASH_WINDOW_SIZE,
-	.bankwidth = FLASH_BUSWIDTH,
-	.phys = FLASH_WINDOW_ADDR,
-};
-
-struct map_info ocelot_nvram_map = {
-	.name = "Ocelot NVRAM",
-	.size = NVRAM_WINDOW_SIZE,
-	.bankwidth = NVRAM_BUSWIDTH,
-	.phys = NVRAM_WINDOW_ADDR,
-};
-
-static const char *probes[] = { "RedBoot", NULL };
-
-static int __init init_ocelot_maps(void)
-{
-	void *pld;
-	int nr_parts;
-	unsigned char brd_status;
-
-       	printk(KERN_INFO "Momenco Ocelot MTD mappings: Flash 0x%x at 0x%x, NVRAM 0x%x at 0x%x\n",
-	       FLASH_WINDOW_SIZE, FLASH_WINDOW_ADDR, NVRAM_WINDOW_SIZE, NVRAM_WINDOW_ADDR);
-
-	/* First check whether the flash jumper is present */
-	pld = ioremap(OCELOT_PLD, 0x10);
-	if (!pld) {
-		printk(KERN_NOTICE "Failed to ioremap Ocelot PLD\n");
-		return -EIO;
-	}
-	brd_status = readb(pld+4);
-	iounmap(pld);
-
-	/* Now ioremap the NVRAM space */
-	ocelot_nvram_map.virt = ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
-	if (!ocelot_nvram_map.virt) {
-		printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n");
-		return -EIO;
-	}
-
-	simple_map_init(&ocelot_nvram_map);
-
-	/* And do the RAM probe on it to get an MTD device */
-	nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map);
-	if (!nvram_mtd) {
-		printk("NVRAM probe failed\n");
-		goto fail_1;
-	}
-	nvram_mtd->owner = THIS_MODULE;
-	nvram_mtd->erasesize = 16;
-	/* Override the write() method */
-	nvram_mtd->write = ocelot_ram_write;
-
-	/* Now map the flash space */
-	ocelot_flash_map.virt = ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
-	if (!ocelot_flash_map.virt) {
-		printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n");
-		goto fail_2;
-	}
-	/* Now the cached version */
-	ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
-
-	simple_map_init(&ocelot_flash_map);
-
-	/* Only probe for flash if the write jumper is present */
-	if (brd_status & 0x40) {
-		flash_mtd = do_map_probe("jedec", &ocelot_flash_map);
-	} else {
-		printk(KERN_NOTICE "Ocelot flash write jumper not present. Treating as ROM\n");
-	}
-	/* If that failed or the jumper's absent, pretend it's ROM */
-	if (!flash_mtd) {
-		flash_mtd = do_map_probe("map_rom", &ocelot_flash_map);
-		/* If we're treating it as ROM, set the erase size */
-		if (flash_mtd)
-			flash_mtd->erasesize = 0x10000;
-	}
-	if (!flash_mtd)
-		goto fail3;
-
-	add_mtd_device(nvram_mtd);
-
-	flash_mtd->owner = THIS_MODULE;
-	nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
-
-	if (nr_parts > 0)
-		add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
-	else
-		add_mtd_device(flash_mtd);
-
-	return 0;
-
- fail3:
-	iounmap((void *)ocelot_flash_map.virt);
-	if (ocelot_flash_map.cached)
-			iounmap((void *)ocelot_flash_map.cached);
- fail_2:
-	map_destroy(nvram_mtd);
- fail_1:
-	iounmap((void *)ocelot_nvram_map.virt);
-
-	return -ENXIO;
-}
-
-static void __exit cleanup_ocelot_maps(void)
-{
-	del_mtd_device(nvram_mtd);
-	map_destroy(nvram_mtd);
-	iounmap((void *)ocelot_nvram_map.virt);
-
-	if (parsed_parts)
-		del_mtd_partitions(flash_mtd);
-	else
-		del_mtd_device(flash_mtd);
-	map_destroy(flash_mtd);
-	iounmap((void *)ocelot_flash_map.virt);
-	if (ocelot_flash_map.cached)
-		iounmap((void *)ocelot_flash_map.cached);
-}
-
-module_init(init_ocelot_maps);
-module_exit(cleanup_ocelot_maps);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>");
-MODULE_DESCRIPTION("MTD map driver for Momenco Ocelot board");
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index cf75a56..aeed9ea 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -232,7 +232,6 @@
 	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	if (!info)
 		goto err_out;
-	memset(info, 0, sizeof(*info));
 
 	dev_set_drvdata(&dev->dev, info);
 
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
index 7e0377e..02bde8c 100644
--- a/drivers/mtd/maps/pmcmsp-flash.c
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -73,13 +73,16 @@
 		return -ENXIO;
 
 	printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
-	msp_flash = (struct mtd_info **)kmalloc(
-			fcnt * sizeof(struct map_info *), GFP_KERNEL);
-	msp_parts = (struct mtd_partition **)kmalloc(
-			fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
-	msp_maps = (struct map_info *)kmalloc(
-			fcnt * sizeof(struct mtd_info), GFP_KERNEL);
-	memset(msp_maps, 0, fcnt * sizeof(struct mtd_info));
+
+	msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
+	msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
+	msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
+	if (!msp_flash || !msp_parts || !msp_maps) {
+		kfree(msp_maps);
+		kfree(msp_parts);
+		kfree(msp_flash);
+		return -ENOMEM;
+	}
 
 	/* loop over the flash devices, initializing each */
 	for (i = 0; i < fcnt; i++) {
@@ -95,9 +98,8 @@
 			continue;
 		}
 
-		msp_parts[i] = (struct mtd_partition *)kmalloc(
-			pcnt * sizeof(struct mtd_partition), GFP_KERNEL);
-		memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition));
+		msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition),
+				       GFP_KERNEL);
 
 		/* now initialize the devices proper */
 		flash_name[5] = '0' + i;
diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c
index 18049bc..30de5c0 100644
--- a/drivers/mtd/maps/pmcmsp-ramroot.c
+++ b/drivers/mtd/maps/pmcmsp-ramroot.c
@@ -79,7 +79,6 @@
 		rr_mtd->owner = THIS_MODULE;
 
 		add_mtd_device(rr_mtd);
-		ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index);
 
 		return 0;
 	}
diff --git a/drivers/mtd/maps/pq2fads.c b/drivers/mtd/maps/pq2fads.c
deleted file mode 100644
index fb78d87..0000000
--- a/drivers/mtd/maps/pq2fads.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * drivers/mtd/maps/pq2fads.c
- *
- * Mapping for the flash SIMM on 8272ADS and PQ2FADS board
- *
- * Author: Vitaly Bordug <vbordug@ru.mvista.com>
- *
- * 2005 (c) MontaVista Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <asm/ppcboot.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-
-/*
-  NOTE: bank width and interleave relative to the installed flash
-  should have been chosen within MTD_CFI_GEOMETRY options.
-  */
-#define PQ2FADS_BANK_WIDTH 4
-
-static struct mtd_partition pq2fads_partitions[] = {
-	{
-#ifdef CONFIG_ADS8272
-		.name		= "HRCW",
-		.size		= 0x40000,
-		.offset 	= 0,
-		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
-	}, {
-		.name		= "User FS",
-		.size		= 0x5c0000,
-		.offset 	= 0x40000,
-#else
-		.name		= "User FS",
-		.size		= 0x600000,
-		.offset 	= 0,
-#endif
-	}, {
-		.name		= "uImage",
-		.size		= 0x100000,
-		.offset 	= 0x600000,
-		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
-	}, {
-		.name		= "bootloader",
-		.size		= 0x40000,
-		.offset		= 0x700000,
-		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
-	}, {
-		.name		= "bootloader env",
-		.size		= 0x40000,
-		.offset		= 0x740000,
-		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
-	}
-};
-
-
-/* pointer to MPC885ADS board info data */
-extern unsigned char __res[];
-
-static int __init init_pq2fads_mtd(void)
-{
-	bd_t *bd = (bd_t *)__res;
-	physmap_configure(bd->bi_flashstart, bd->bi_flashsize, PQ2FADS_BANK_WIDTH, NULL);
-
-	physmap_set_partitions(pq2fads_partitions,
-				sizeof (pq2fads_partitions) /
-				sizeof (pq2fads_partitions[0]));
-	return 0;
-}
-
-static void __exit cleanup_pq2fads_mtd(void)
-{
-}
-
-module_init(init_pq2fads_mtd);
-module_exit(cleanup_pq2fads_mtd);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("MTD map and partitions for MPC8272ADS boards");
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c
new file mode 100644
index 0000000..cb933ac
--- /dev/null
+++ b/drivers/mtd/maps/pxa2xx-flash.c
@@ -0,0 +1,200 @@
+/*
+ * Map driver for Intel XScale PXA2xx platforms.
+ *
+ * Author:	Nicolas Pitre
+ * Copyright:	(C) 2001 MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include <asm/mach/flash.h>
+
+static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from,
+				      ssize_t len)
+{
+	consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE);
+}
+
+struct pxa2xx_flash_info {
+	struct mtd_partition	*parts;
+	int			nr_parts;
+	struct mtd_info		*mtd;
+	struct map_info		map;
+};
+
+
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+
+static int __init pxa2xx_flash_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct flash_platform_data *flash = pdev->dev.platform_data;
+	struct pxa2xx_flash_info *info;
+	struct mtd_partition *parts;
+	struct resource *res;
+	int ret = 0;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	info = kmalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	memset(info, 0, sizeof(struct pxa2xx_flash_info));
+	info->map.name = (char *) flash->name;
+	info->map.bankwidth = flash->width;
+	info->map.phys = res->start;
+	info->map.size = res->end - res->start + 1;
+	info->parts = flash->parts;
+	info->nr_parts = flash->nr_parts;
+
+	info->map.virt = ioremap(info->map.phys, info->map.size);
+	if (!info->map.virt) {
+		printk(KERN_WARNING "Failed to ioremap %s\n",
+		       info->map.name);
+		return -ENOMEM;
+	}
+	info->map.cached =
+		ioremap_cached(info->map.phys, info->map.size);
+	if (!info->map.cached)
+		printk(KERN_WARNING "Failed to ioremap cached %s\n",
+		       info->map.name);
+	info->map.inval_cache = pxa2xx_map_inval_cache;
+	simple_map_init(&info->map);
+
+	printk(KERN_NOTICE
+	       "Probing %s at physical address 0x%08lx"
+	       " (%d-bit bankwidth)\n",
+	       info->map.name, (unsigned long)info->map.phys,
+	       info->map.bankwidth * 8);
+
+	info->mtd = do_map_probe(flash->map_name, &info->map);
+
+	if (!info->mtd) {
+		iounmap((void *)info->map.virt);
+		if (info->map.cached)
+			iounmap(info->map.cached);
+		return -EIO;
+	}
+	info->mtd->owner = THIS_MODULE;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	ret = parse_mtd_partitions(info->mtd, probes, &parts, 0);
+
+	if (ret > 0) {
+		info->nr_parts = ret;
+		info->parts = parts;
+	}
+#endif
+
+	if (info->nr_parts) {
+		add_mtd_partitions(info->mtd, info->parts,
+				   info->nr_parts);
+	} else {
+		printk("Registering %s as whole device\n",
+		       info->map.name);
+		add_mtd_device(info->mtd);
+	}
+
+	dev_set_drvdata(dev, info);
+	return 0;
+}
+
+static int __exit pxa2xx_flash_remove(struct device *dev)
+{
+	struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
+
+	dev_set_drvdata(dev, NULL);
+
+#ifdef CONFIG_MTD_PARTITIONS
+	if (info->nr_parts)
+		del_mtd_partitions(info->mtd);
+	else
+#endif
+		del_mtd_device(info->mtd);
+
+	map_destroy(info->mtd);
+	iounmap(info->map.virt);
+	if (info->map.cached)
+		iounmap(info->map.cached);
+	kfree(info->parts);
+	kfree(info);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxa2xx_flash_suspend(struct device *dev, pm_message_t state)
+{
+	struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (info->mtd && info->mtd->suspend)
+		ret = info->mtd->suspend(info->mtd);
+	return ret;
+}
+
+static int pxa2xx_flash_resume(struct device *dev)
+{
+	struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
+
+	if (info->mtd && info->mtd->resume)
+		info->mtd->resume(info->mtd);
+	return 0;
+}
+static void pxa2xx_flash_shutdown(struct device *dev)
+{
+	struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
+
+	if (info && info->mtd->suspend(info->mtd) == 0)
+		info->mtd->resume(info->mtd);
+}
+#else
+#define pxa2xx_flash_suspend NULL
+#define pxa2xx_flash_resume NULL
+#define pxa2xx_flash_shutdown NULL
+#endif
+
+static struct device_driver pxa2xx_flash_driver = {
+	.name		= "pxa2xx-flash",
+	.bus		= &platform_bus_type,
+	.probe		= pxa2xx_flash_probe,
+	.remove		= __exit_p(pxa2xx_flash_remove),
+	.suspend	= pxa2xx_flash_suspend,
+	.resume		= pxa2xx_flash_resume,
+	.shutdown	= pxa2xx_flash_shutdown,
+};
+
+static int __init init_pxa2xx_flash(void)
+{
+	return driver_register(&pxa2xx_flash_driver);
+}
+
+static void __exit cleanup_pxa2xx_flash(void)
+{
+	driver_unregister(&pxa2xx_flash_driver);
+}
+
+module_init(init_pxa2xx_flash);
+module_exit(cleanup_pxa2xx_flash);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
+MODULE_DESCRIPTION("MTD map driver for Intel XScale PXA2xx");
diff --git a/drivers/mtd/maps/tqm834x.c b/drivers/mtd/maps/tqm834x.c
deleted file mode 100644
index 9adc970..0000000
--- a/drivers/mtd/maps/tqm834x.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * drivers/mtd/maps/tqm834x.c
- *
- * MTD mapping driver for TQM834x boards
- *
- * Copyright 2005 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>.
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2.  This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <asm/io.h>
-#include <asm/ppcboot.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#define FLASH_BANK_MAX	2
-
-extern unsigned char __res[];
-
-/* trivial struct to describe partition information */
-struct mtd_part_def
-{
-	int nums;
-	unsigned char *type;
-	struct mtd_partition* mtd_part;
-};
-
-static struct mtd_info* mtd_banks[FLASH_BANK_MAX];
-static struct map_info* map_banks[FLASH_BANK_MAX];
-static struct mtd_part_def part_banks[FLASH_BANK_MAX];
-
-static unsigned long num_banks;
-static unsigned long start_scan_addr;
-
-#ifdef CONFIG_MTD_PARTITIONS
-/*
- * The following defines the partition layout of TQM834x boards.
- *
- * See include/linux/mtd/partitions.h for definition of the
- * mtd_partition structure.
- *
- * Assume minimal initial size of 4 MiB per bank, will be updated
- * later in init_tqm834x_mtd() routine.
- */
-
-/* Partition definition for the first flash bank which is always present. */
-static struct mtd_partition tqm834x_partitions_bank1[] = {
-	{
-		.name	= "u-boot",		/* u-boot firmware	*/
-		.offset	= 0x00000000,
-		.size	= 0x00040000,		/* 256 KiB		*/
-		/*mask_flags: MTD_WRITEABLE,	 * force read-only	*/
-	},
-	{
-		.name	= "env",		/* u-boot environment	*/
-		.offset	= 0x00040000,
-		.size	= 0x00020000,		/* 128 KiB		*/
-		/*mask_flags: MTD_WRITEABLE,	 * force read-only	*/
-	},
-	{
-		.name	= "kernel",		/* linux kernel image	*/
-		.offset	= 0x00060000,
-		.size	= 0x00100000,		/* 1 MiB		*/
-		/*mask_flags: MTD_WRITEABLE,	 * force read-only	*/
-	},
-	{
-		.name	= "initrd",		/* ramdisk image	*/
-		.offset	= 0x00160000,
-		.size	= 0x00200000,		/* 2 MiB		*/
-	},
-	{
-		.name	= "user",		/* user data		*/
-		.offset	= 0x00360000,
-		.size	= 0x000a0000,		/* remaining space	*/
-		/* NOTE: this parttion size is re-calcated in		*/
-		/* init_tqm834x_mtd() to cover actual remaining space.	*/
-	},
-};
-
-/* Partition definition for the second flash bank which may be present on some
- * TQM834x boards.
- */
-static struct mtd_partition tqm834x_partitions_bank2[] = {
-	{
-		.name	= "jffs2",		/* jffs2 filesystem	*/
-		.offset	= 0x00000000,
-		.size	= 0x00400000,		/* whole device		*/
-		/* NOTE: this parttion size is re-calcated in		*/
-		/* init_tqm834x_mtd() to cover actual device size.	*/
-	},
-};
-
-#endif	/* CONFIG_MTD_PARTITIONS */
-
-static int __init init_tqm834x_mtd(void)
-{
-	int idx = 0, ret = 0;
-	unsigned long flash_addr, flash_size, mtd_size = 0;
-
-	/* pointer to TQM834x board info data */
-	bd_t *bd = (bd_t *)__res;
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-	int n;
-	char mtdid[4];
-	const char *part_probes[] = { "cmdlinepart", NULL };
-#endif
-
-	flash_addr = bd->bi_flashstart;
-	flash_size = bd->bi_flashsize;
-
-	/* request maximum flash size address space */
-	start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size);
-	if (!start_scan_addr) {
-		printk("%s: Failed to ioremap address: 0x%lx\n",
-		       __FUNCTION__, flash_addr);
-		return -EIO;
-	}
-
-	for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
-		if (mtd_size >= flash_size)
-			break;
-
-		pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx);
-
-		map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL);
-		if (map_banks[idx] == NULL) {
-			ret = -ENOMEM;
-			goto error_mem;
-		}
-		map_banks[idx]->name = kzalloc(16, GFP_KERNEL);
-		if (map_banks[idx]->name == NULL) {
-			ret = -ENOMEM;
-			goto error_mem;
-		}
-
-		sprintf(map_banks[idx]->name, "TQM834x-%d", idx);
-		map_banks[idx]->size = flash_size;
-		map_banks[idx]->bankwidth = 4;
-
-		simple_map_init(map_banks[idx]);
-
-		map_banks[idx]->virt = (void __iomem *)
-			(start_scan_addr + ((idx > 0) ?
-			(mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0));
-		map_banks[idx]->phys =
-			flash_addr + ((idx > 0) ?
-			(mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0);
-
-		/* start to probe flash chips */
-		mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]);
-		if (mtd_banks[idx]) {
-			mtd_banks[idx]->owner = THIS_MODULE;
-			mtd_size += mtd_banks[idx]->size;
-			num_banks++;
-			pr_debug("%s: bank %ld, name: %s, size: %d bytes \n",
-				 __FUNCTION__, num_banks,
-				 mtd_banks[idx]->name, mtd_banks[idx]->size);
-		}
-	}
-
-	/* no supported flash chips found */
-	if (!num_banks) {
-		printk("TQM834x: No supported flash chips found!\n");
-		ret = -ENXIO;
-		goto error_mem;
-	}
-
-#ifdef CONFIG_MTD_PARTITIONS
-	/*
-	 * Select static partition definitions
-	 */
-	n = ARRAY_SIZE(tqm834x_partitions_bank1);
-	part_banks[0].mtd_part	= tqm834x_partitions_bank1;
-	part_banks[0].type	= "static image bank1";
-	part_banks[0].nums	= n;
-
-	/* update last partition size to cover actual remaining space */
-	tqm834x_partitions_bank1[n - 1].size =
-		mtd_banks[0]->size -
-		tqm834x_partitions_bank1[n - 1].offset;
-
-	/* check if we have second bank? */
-	if (num_banks == 2) {
-		n = ARRAY_SIZE(tqm834x_partitions_bank2);
-		part_banks[1].mtd_part	= tqm834x_partitions_bank2;
-		part_banks[1].type	= "static image bank2";
-		part_banks[1].nums	= n;
-
-		/* update last partition size to cover actual remaining space */
-		tqm834x_partitions_bank2[n - 1].size =
-			mtd_banks[1]->size -
-			tqm834x_partitions_bank2[n - 1].offset;
-	}
-
-	for(idx = 0; idx < num_banks ; idx++) {
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-		sprintf(mtdid, "%d", idx);
-		n = parse_mtd_partitions(mtd_banks[idx],
-					 part_probes,
-					 &part_banks[idx].mtd_part,
-					 0);
-		pr_debug("%s: %d command line partitions on bank %s\n",
-			 __FUNCTION__, n, mtdid);
-		if (n > 0) {
-			part_banks[idx].type = "command line";
-			part_banks[idx].nums = n;
-		}
-#endif	/* CONFIG_MTD_CMDLINE_PARTS */
-		if (part_banks[idx].nums == 0) {
-			printk(KERN_NOTICE
-			       "TQM834x flash bank %d: no partition info "
-			       "available, registering whole device\n", idx);
-			add_mtd_device(mtd_banks[idx]);
-		} else {
-			printk(KERN_NOTICE
-			       "TQM834x flash bank %d: Using %s partition "
-			       "definition\n", idx, part_banks[idx].type);
-			add_mtd_partitions(mtd_banks[idx],
-					   part_banks[idx].mtd_part,
-					   part_banks[idx].nums);
-		}
-	}
-#else	/* ! CONFIG_MTD_PARTITIONS */
-	printk(KERN_NOTICE "TQM834x flash: registering %d flash banks "
-			"at once\n", num_banks);
-
-	for(idx = 0 ; idx < num_banks ; idx++)
-		add_mtd_device(mtd_banks[idx]);
-
-#endif	/* CONFIG_MTD_PARTITIONS */
-
-	return 0;
-error_mem:
-	for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
-		if (map_banks[idx] != NULL) {
-			if (map_banks[idx]->name != NULL) {
-				kfree(map_banks[idx]->name);
-				map_banks[idx]->name = NULL;
-			}
-			kfree(map_banks[idx]);
-			map_banks[idx] = NULL;
-		}
-	}
-
-	iounmap((void *)start_scan_addr);
-
-	return ret;
-}
-
-static void __exit cleanup_tqm834x_mtd(void)
-{
-	unsigned int idx = 0;
-	for(idx = 0 ; idx < num_banks ; idx++) {
-		/* destroy mtd_info previously allocated */
-		if (mtd_banks[idx]) {
-			del_mtd_partitions(mtd_banks[idx]);
-			map_destroy(mtd_banks[idx]);
-		}
-
-		/* release map_info not used anymore */
-		kfree(map_banks[idx]->name);
-		kfree(map_banks[idx]);
-	}
-
-	if (start_scan_addr) {
-		iounmap((void *)start_scan_addr);
-		start_scan_addr = 0;
-	}
-}
-
-module_init(init_tqm834x_mtd);
-module_exit(cleanup_tqm834x_mtd);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Wolfgang Denk <wd@denx.de>");
-MODULE_DESCRIPTION("MTD map driver for TQM834x boards");
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index ef89780..74d9d30 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -24,10 +24,9 @@
 #include <linux/kthread.h>
 #include <asm/uaccess.h>
 
-static LIST_HEAD(blktrans_majors);
+#include "mtdcore.h"
 
-extern struct mutex mtd_table_mutex;
-extern struct mtd_info *mtd_table[];
+static LIST_HEAD(blktrans_majors);
 
 struct mtd_blkcore_priv {
 	struct task_struct *thread;
@@ -202,7 +201,7 @@
 	}
 }
 
-struct block_device_operations mtd_blktrans_ops = {
+static struct block_device_operations mtd_blktrans_ops = {
 	.owner		= THIS_MODULE,
 	.open		= blktrans_open,
 	.release	= blktrans_release,
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index d091b24..22ed96c 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -136,7 +136,8 @@
 
 	DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 
-	if (mtd->sync)
+	/* Only sync if opened RW */
+	if ((file->f_mode & 2) && mtd->sync)
 		mtd->sync(mtd);
 
 	put_mtd_device(mtd);
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 41844ea..96be7ef 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -178,7 +178,7 @@
 
 	/* Check alignment */
 	if (mtd->writesize > 1) {
-		loff_t __to = to;
+		uint64_t __to = to;
 		if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
 			return -EINVAL;
 	}
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index c153b64..6c2645e 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -22,6 +22,8 @@
 
 #include <linux/mtd/mtd.h>
 
+#include "mtdcore.h"
+
 /* These are exported solely for the purpose of mtd_blkdevs.c. You
    should not use them for _anything_ else */
 DEFINE_MUTEX(mtd_table_mutex);
diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
new file mode 100644
index 0000000..a33251f
--- /dev/null
+++ b/drivers/mtd/mtdcore.h
@@ -0,0 +1,11 @@
+/* linux/drivers/mtd/mtdcore.h
+ *
+ * Header file for driver private mtdcore exports
+ *
+ */
+
+/* These are exported solely for the purpose of mtd_blkdevs.c. You
+   should not use them for _anything_ else */
+
+extern struct mutex mtd_table_mutex;
+extern struct mtd_info *mtd_table[MAX_MTD_DEVICES];
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
new file mode 100644
index 0000000..f8af627
--- /dev/null
+++ b/drivers/mtd/mtdoops.c
@@ -0,0 +1,376 @@
+/*
+ * MTD Oops/Panic logger
+ *
+ * Copyright (C) 2007 Nokia Corporation. All rights reserved.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/mtd/mtd.h>
+
+#define OOPS_PAGE_SIZE 4096
+
+static struct mtdoops_context {
+	int mtd_index;
+	struct work_struct work;
+	struct mtd_info *mtd;
+	int oops_pages;
+	int nextpage;
+	int nextcount;
+
+	void *oops_buf;
+	int ready;
+	int writecount;
+} oops_cxt;
+
+static void mtdoops_erase_callback(struct erase_info *done)
+{
+	wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
+	wake_up(wait_q);
+}
+
+static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
+{
+	struct erase_info erase;
+	DECLARE_WAITQUEUE(wait, current);
+	wait_queue_head_t wait_q;
+	int ret;
+
+	init_waitqueue_head(&wait_q);
+	erase.mtd = mtd;
+	erase.callback = mtdoops_erase_callback;
+	erase.addr = offset;
+	if (mtd->erasesize < OOPS_PAGE_SIZE)
+		erase.len = OOPS_PAGE_SIZE;
+	else
+		erase.len = mtd->erasesize;
+	erase.priv = (u_long)&wait_q;
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&wait_q, &wait);
+
+	ret = mtd->erase(mtd, &erase);
+	if (ret) {
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&wait_q, &wait);
+		printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] "
+				     "on \"%s\" failed\n",
+			erase.addr, erase.len, mtd->name);
+		return ret;
+	}
+
+	schedule();  /* Wait for erase to finish. */
+	remove_wait_queue(&wait_q, &wait);
+
+	return 0;
+}
+
+static int mtdoops_inc_counter(struct mtdoops_context *cxt)
+{
+	struct mtd_info *mtd = cxt->mtd;
+	size_t retlen;
+	u32 count;
+	int ret;
+
+	cxt->nextpage++;
+	if (cxt->nextpage > cxt->oops_pages)
+		cxt->nextpage = 0;
+	cxt->nextcount++;
+	if (cxt->nextcount == 0xffffffff)
+		cxt->nextcount = 0;
+
+	ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4,
+			&retlen, (u_char *) &count);
+	if ((retlen != 4) || (ret < 0)) {
+		printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)"
+				", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE,
+				retlen, ret);
+		return 1;
+	}
+
+	/* See if we need to erase the next block */
+	if (count != 0xffffffff)
+		return 1;
+
+	printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n",
+			cxt->nextpage, cxt->nextcount);
+	cxt->ready = 1;
+	return 0;
+}
+
+static void mtdoops_prepare(struct mtdoops_context *cxt)
+{
+	struct mtd_info *mtd = cxt->mtd;
+	int i = 0, j, ret, mod;
+
+	/* We were unregistered */
+	if (!mtd)
+		return;
+
+	mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize;
+	if (mod != 0) {
+		cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE);
+		if (cxt->nextpage > cxt->oops_pages)
+			cxt->nextpage = 0;
+	}
+
+	while (mtd->block_isbad &&
+			mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) {
+badblock:
+		printk(KERN_WARNING "mtdoops: Bad block at %08x\n",
+				cxt->nextpage * OOPS_PAGE_SIZE);
+		i++;
+		cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE);
+		if (cxt->nextpage > cxt->oops_pages)
+			cxt->nextpage = 0;
+		if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) {
+			printk(KERN_ERR "mtdoops: All blocks bad!\n");
+			return;
+		}
+	}
+
+	for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
+		ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
+
+	if (ret < 0) {
+		if (mtd->block_markbad)
+			mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
+		goto badblock;
+	}
+
+	printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount);
+
+	cxt->ready = 1;
+}
+
+static void mtdoops_workfunc(struct work_struct *work)
+{
+	struct mtdoops_context *cxt =
+			container_of(work, struct mtdoops_context, work);
+
+	mtdoops_prepare(cxt);
+}
+
+static int find_next_position(struct mtdoops_context *cxt)
+{
+	struct mtd_info *mtd = cxt->mtd;
+	int page, maxpos = 0;
+	u32 count, maxcount = 0xffffffff;
+	size_t retlen;
+
+	for (page = 0; page < cxt->oops_pages; page++) {
+		mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count);
+		if (count == 0xffffffff)
+			continue;
+		if (maxcount == 0xffffffff) {
+			maxcount = count;
+			maxpos = page;
+		} else if ((count < 0x40000000) && (maxcount > 0xc0000000)) {
+			maxcount = count;
+			maxpos = page;
+		} else if ((count > maxcount) && (count < 0xc0000000)) {
+			maxcount = count;
+			maxpos = page;
+		} else if ((count > maxcount) && (count > 0xc0000000)
+					&& (maxcount > 0x80000000)) {
+			maxcount = count;
+			maxpos = page;
+		}
+	}
+	if (maxcount == 0xffffffff) {
+		cxt->nextpage = 0;
+		cxt->nextcount = 1;
+		cxt->ready = 1;
+		printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n",
+				cxt->nextpage, cxt->nextcount);
+		return 0;
+	}
+
+	cxt->nextpage = maxpos;
+	cxt->nextcount = maxcount;
+
+	return mtdoops_inc_counter(cxt);
+}
+
+
+static void mtdoops_notify_add(struct mtd_info *mtd)
+{
+	struct mtdoops_context *cxt = &oops_cxt;
+	int ret;
+
+	if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
+		return;
+
+	if (mtd->size < (mtd->erasesize * 2)) {
+		printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n",
+				mtd->index);
+		return;
+	}
+
+	cxt->mtd = mtd;
+	cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE;
+
+	ret = find_next_position(cxt);
+	if (ret == 1)
+		mtdoops_prepare(cxt);
+
+	printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index);
+}
+
+static void mtdoops_notify_remove(struct mtd_info *mtd)
+{
+	struct mtdoops_context *cxt = &oops_cxt;
+
+	if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
+		return;
+
+	cxt->mtd = NULL;
+	flush_scheduled_work();
+}
+
+static void mtdoops_console_sync(void)
+{
+	struct mtdoops_context *cxt = &oops_cxt;
+	struct mtd_info *mtd = cxt->mtd;
+	size_t retlen;
+	int ret;
+
+	if (!cxt->ready || !mtd)
+		return;
+
+	if (cxt->writecount == 0)
+		return;
+
+	if (cxt->writecount < OOPS_PAGE_SIZE)
+		memset(cxt->oops_buf + cxt->writecount, 0xff,
+					OOPS_PAGE_SIZE - cxt->writecount);
+
+	ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE,
+					OOPS_PAGE_SIZE, &retlen, cxt->oops_buf);
+	cxt->ready = 0;
+	cxt->writecount = 0;
+
+	if ((retlen != OOPS_PAGE_SIZE) || (ret < 0))
+		printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n",
+			cxt->nextpage * OOPS_PAGE_SIZE, retlen,	OOPS_PAGE_SIZE, ret);
+
+	ret = mtdoops_inc_counter(cxt);
+	if (ret == 1)
+		schedule_work(&cxt->work);
+}
+
+static void
+mtdoops_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct mtdoops_context *cxt = co->data;
+	struct mtd_info *mtd = cxt->mtd;
+	int i;
+
+	if (!oops_in_progress) {
+		mtdoops_console_sync();
+		return;
+	}
+
+	if (!cxt->ready || !mtd)
+		return;
+
+	if (cxt->writecount == 0) {
+		u32 *stamp = cxt->oops_buf;
+		*stamp = cxt->nextcount;
+		cxt->writecount = 4;
+	}
+
+	if ((count + cxt->writecount) > OOPS_PAGE_SIZE)
+		count = OOPS_PAGE_SIZE - cxt->writecount;
+
+	for (i = 0; i < count; i++, s++)
+		*((char *)(cxt->oops_buf) + cxt->writecount + i) = *s;
+
+	cxt->writecount = cxt->writecount + count;
+}
+
+static int __init mtdoops_console_setup(struct console *co, char *options)
+{
+	struct mtdoops_context *cxt = co->data;
+
+	if (cxt->mtd_index != -1)
+		return -EBUSY;
+	if (co->index == -1)
+		return -EINVAL;
+
+	cxt->mtd_index = co->index;
+	return 0;
+}
+
+static struct mtd_notifier mtdoops_notifier = {
+	.add	= mtdoops_notify_add,
+	.remove	= mtdoops_notify_remove,
+};
+
+static struct console mtdoops_console = {
+	.name		= "ttyMTD",
+	.write		= mtdoops_console_write,
+	.setup		= mtdoops_console_setup,
+	.unblank	= mtdoops_console_sync,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &oops_cxt,
+};
+
+static int __init mtdoops_console_init(void)
+{
+	struct mtdoops_context *cxt = &oops_cxt;
+
+	cxt->mtd_index = -1;
+	cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE);
+
+	if (!cxt->oops_buf) {
+		printk(KERN_ERR "Failed to allocate oops buffer workspace\n");
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&cxt->work, mtdoops_workfunc);
+
+	register_console(&mtdoops_console);
+	register_mtd_user(&mtdoops_notifier);
+	return 0;
+}
+
+static void __exit mtdoops_console_exit(void)
+{
+	struct mtdoops_context *cxt = &oops_cxt;
+
+	unregister_mtd_user(&mtdoops_notifier);
+	unregister_console(&mtdoops_console);
+	vfree(cxt->oops_buf);
+}
+
+
+subsys_initcall(mtdoops_console_init);
+module_exit(mtdoops_console_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver");
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f1d60b6..8f9c3ba 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -91,6 +91,25 @@
 	  This enables the driver for the NAND flash controller on the
 	  AMD/Alchemy 1550 SOC.
 
+config MTD_NAND_BF5XX
+	tristate "Blackfin on-chip NAND Flash Controller driver"
+	depends on BF54x && MTD_NAND
+	help
+	  This enables the Blackfin on-chip NAND flash controller
+
+	  No board specific support is done by this driver, each board
+	  must advertise a platform_device for the driver to attach.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called bf5xx-nand.
+
+config MTD_NAND_BF5XX_HWECC
+	bool "BF5XX NAND Hardware ECC"
+	depends on MTD_NAND_BF5XX
+	help
+	  Enable the use of the BF5XX's internal ECC generator when
+	  using NAND.
+
 config MTD_NAND_RTC_FROM4
 	tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
 	depends on SH_SOLUTION_ENGINE
@@ -134,10 +153,10 @@
 
 config MTD_NAND_NDFC
 	tristate "NDFC NanD Flash Controller"
-	depends on 44x
+	depends on 4xx && !PPC_MERGE
 	select MTD_NAND_ECC_SMC
 	help
-	 NDFC Nand Flash Controllers are integrated in EP44x SoCs
+	 NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
 
 config MTD_NAND_S3C2410_CLKSTOP
 	bool "S3C2410 NAND IDLE clock stop"
@@ -237,7 +256,7 @@
 	select REED_SOLOMON
 	select REED_SOLOMON_DEC16
 	help
-	  Use NAND flash attached to the CAFÉ chip designed for the $100
+	  Use NAND flash attached to the CAFÉ chip designed for the OLPC
 	  laptop.
 
 config MTD_NAND_CS553X
@@ -280,5 +299,11 @@
 	  devices. You will need to provide platform-specific functions
 	  via platform_data.
 
+config MTD_ALAUDA
+	tristate "MTD driver for Olympus MAUSB-10 and Fijufilm DPC-R1"
+	depends on MTD_NAND && USB
+	help
+	  These two (and possibly other) Alauda-based cardreaders for
+	  SmartMedia and xD allow raw flash access.
 
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index edba1db..3ad6c01 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -13,6 +13,7 @@
 obj-$(CONFIG_MTD_NAND_AUTCPU12)		+= autcpu12.o
 obj-$(CONFIG_MTD_NAND_EDB7312)		+= edb7312.o
 obj-$(CONFIG_MTD_NAND_AU1550)		+= au1550nd.o
+obj-$(CONFIG_MTD_NAND_BF5XX)		+= bf5xx_nand.o
 obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB)	+= ppchameleonevb.o
 obj-$(CONFIG_MTD_NAND_S3C2410)		+= s3c2410.o
 obj-$(CONFIG_MTD_NAND_DISKONCHIP)	+= diskonchip.o
@@ -27,5 +28,6 @@
 obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)	+= excite_nandflash.o
 obj-$(CONFIG_MTD_NAND_PLATFORM)		+= plat_nand.o
+obj-$(CONFIG_MTD_ALAUDA)		+= alauda.o
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c
new file mode 100644
index 0000000..257937c
--- /dev/null
+++ b/drivers/mtd/nand/alauda.c
@@ -0,0 +1,742 @@
+/*
+ * MTD driver for Alauda chips
+ *
+ * Copyright (C) 2007 Joern Engel <joern@logfs.org>
+ *
+ * Based on drivers/usb/usb-skeleton.c which is:
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * and on drivers/usb/storage/alauda.c, which is:
+ *   (c) 2005 Daniel Drake <dsd@gentoo.org>
+ *
+ * Idea and initial work by Arnd Bergmann <arnd@arndb.de>
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand_ecc.h>
+
+/* Control commands */
+#define ALAUDA_GET_XD_MEDIA_STATUS	0x08
+#define ALAUDA_ACK_XD_MEDIA_CHANGE	0x0a
+#define ALAUDA_GET_XD_MEDIA_SIG		0x86
+
+/* Common prefix */
+#define ALAUDA_BULK_CMD			0x40
+
+/* The two ports */
+#define ALAUDA_PORT_XD			0x00
+#define ALAUDA_PORT_SM			0x01
+
+/* Bulk commands */
+#define ALAUDA_BULK_READ_PAGE		0x84
+#define ALAUDA_BULK_READ_OOB		0x85 /* don't use, there's a chip bug */
+#define ALAUDA_BULK_READ_BLOCK		0x94
+#define ALAUDA_BULK_ERASE_BLOCK		0xa3
+#define ALAUDA_BULK_WRITE_PAGE		0xa4
+#define ALAUDA_BULK_WRITE_BLOCK		0xb4
+#define ALAUDA_BULK_RESET_MEDIA		0xe0
+
+/* Address shifting */
+#define PBA_LO(pba) ((pba & 0xF) << 5)
+#define PBA_HI(pba) (pba >> 3)
+#define PBA_ZONE(pba) (pba >> 11)
+
+#define TIMEOUT HZ
+
+static struct usb_device_id alauda_table [] = {
+	{ USB_DEVICE(0x0584, 0x0008) },	/* Fujifilm DPC-R1 */
+	{ USB_DEVICE(0x07b4, 0x010a) },	/* Olympus MAUSB-10 */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, alauda_table);
+
+struct alauda_card {
+	u8	id;		/* id byte */
+	u8	chipshift;	/* 1<<chipshift total size */
+	u8	pageshift;	/* 1<<pageshift page size */
+	u8	blockshift;	/* 1<<blockshift block size */
+};
+
+struct alauda {
+	struct usb_device	*dev;
+	struct usb_interface	*interface;
+	struct mtd_info		*mtd;
+	struct alauda_card	*card;
+	struct mutex		card_mutex;
+	u32			pagemask;
+	u32			bytemask;
+	u32			blockmask;
+	unsigned int		write_out;
+	unsigned int		bulk_in;
+	unsigned int		bulk_out;
+	u8			port;
+	struct kref		kref;
+};
+
+static struct alauda_card alauda_card_ids[] = {
+	/* NAND flash */
+	{ 0x6e, 20, 8, 12},	/* 1 MB */
+	{ 0xe8, 20, 8, 12},	/* 1 MB */
+	{ 0xec, 20, 8, 12},	/* 1 MB */
+	{ 0x64, 21, 8, 12},	/* 2 MB */
+	{ 0xea, 21, 8, 12},	/* 2 MB */
+	{ 0x6b, 22, 9, 13},	/* 4 MB */
+	{ 0xe3, 22, 9, 13},	/* 4 MB */
+	{ 0xe5, 22, 9, 13},	/* 4 MB */
+	{ 0xe6, 23, 9, 13},	/* 8 MB */
+	{ 0x73, 24, 9, 14},	/* 16 MB */
+	{ 0x75, 25, 9, 14},	/* 32 MB */
+	{ 0x76, 26, 9, 14},	/* 64 MB */
+	{ 0x79, 27, 9, 14},	/* 128 MB */
+	{ 0x71, 28, 9, 14},	/* 256 MB */
+
+	/* MASK ROM */
+	{ 0x5d, 21, 9, 13},	/* 2 MB */
+	{ 0xd5, 22, 9, 13},	/* 4 MB */
+	{ 0xd6, 23, 9, 13},	/* 8 MB */
+	{ 0x57, 24, 9, 13},	/* 16 MB */
+	{ 0x58, 25, 9, 13},	/* 32 MB */
+	{ }
+};
+
+static struct alauda_card *get_card(u8 id)
+{
+	struct alauda_card *card;
+
+	for (card = alauda_card_ids; card->id; card++)
+		if (card->id == id)
+			return card;
+	return NULL;
+}
+
+static void alauda_delete(struct kref *kref)
+{
+	struct alauda *al = container_of(kref, struct alauda, kref);
+
+	if (al->mtd) {
+		del_mtd_device(al->mtd);
+		kfree(al->mtd);
+	}
+	usb_put_dev(al->dev);
+	kfree(al);
+}
+
+static int alauda_get_media_status(struct alauda *al, void *buf)
+{
+	int ret;
+
+	mutex_lock(&al->card_mutex);
+	ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0),
+			ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ);
+	mutex_unlock(&al->card_mutex);
+	return ret;
+}
+
+static int alauda_ack_media(struct alauda *al)
+{
+	int ret;
+
+	mutex_lock(&al->card_mutex);
+	ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0),
+			ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ);
+	mutex_unlock(&al->card_mutex);
+	return ret;
+}
+
+static int alauda_get_media_signatures(struct alauda *al, void *buf)
+{
+	int ret;
+
+	mutex_lock(&al->card_mutex);
+	ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0),
+			ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ);
+	mutex_unlock(&al->card_mutex);
+	return ret;
+}
+
+static void alauda_reset(struct alauda *al)
+{
+	u8 command[] = {
+		ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0,
+		0, 0, 0, 0, al->port
+	};
+	mutex_lock(&al->card_mutex);
+	usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ);
+	mutex_unlock(&al->card_mutex);
+}
+
+static void correct_data(void *buf, void *read_ecc,
+		int *corrected, int *uncorrected)
+{
+	u8 calc_ecc[3];
+	int err;
+
+	nand_calculate_ecc(NULL, buf, calc_ecc);
+	err = nand_correct_data(NULL, buf, read_ecc, calc_ecc);
+	if (err) {
+		if (err > 0)
+			(*corrected)++;
+		else
+			(*uncorrected)++;
+	}
+}
+
+struct alauda_sg_request {
+	struct urb *urb[3];
+	struct completion comp;
+};
+
+static void alauda_complete(struct urb *urb)
+{
+	struct completion *comp = urb->context;
+
+	if (comp)
+		complete(comp);
+}
+
+static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf,
+		void *oob)
+{
+	struct alauda_sg_request sg;
+	struct alauda *al = mtd->priv;
+	u32 pba = from >> al->card->blockshift;
+	u32 page = (from >> al->card->pageshift) & al->pagemask;
+	u8 command[] = {
+		ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba),
+		PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port
+	};
+	int i, err;
+
+	for (i=0; i<3; i++)
+		sg.urb[i] = NULL;
+
+	err = -ENOMEM;
+	for (i=0; i<3; i++) {
+		sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
+		if (!sg.urb[i])
+			goto out;
+	}
+	init_completion(&sg.comp);
+	usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
+			alauda_complete, NULL);
+	usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize,
+			alauda_complete, NULL);
+	usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16,
+			alauda_complete, &sg.comp);
+
+	mutex_lock(&al->card_mutex);
+	for (i=0; i<3; i++) {
+		err = usb_submit_urb(sg.urb[i], GFP_NOIO);
+		if (err)
+			goto cancel;
+	}
+	if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
+		err = -ETIMEDOUT;
+cancel:
+		for (i=0; i<3; i++) {
+			usb_kill_urb(sg.urb[i]);
+		}
+	}
+	mutex_unlock(&al->card_mutex);
+
+out:
+	usb_free_urb(sg.urb[0]);
+	usb_free_urb(sg.urb[1]);
+	usb_free_urb(sg.urb[2]);
+	return err;
+}
+
+static int alauda_read_page(struct mtd_info *mtd, loff_t from,
+		void *buf, u8 *oob, int *corrected, int *uncorrected)
+{
+	int err;
+
+	err = __alauda_read_page(mtd, from, buf, oob);
+	if (err)
+		return err;
+	correct_data(buf, oob+13, corrected, uncorrected);
+	correct_data(buf+256, oob+8, corrected, uncorrected);
+	return 0;
+}
+
+static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf,
+		void *oob)
+{
+	struct alauda_sg_request sg;
+	struct alauda *al = mtd->priv;
+	u32 pba = to >> al->card->blockshift;
+	u32 page = (to >> al->card->pageshift) & al->pagemask;
+	u8 command[] = {
+		ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba),
+		PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port
+	};
+	int i, err;
+
+	for (i=0; i<3; i++)
+		sg.urb[i] = NULL;
+
+	err = -ENOMEM;
+	for (i=0; i<3; i++) {
+		sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
+		if (!sg.urb[i])
+			goto out;
+	}
+	init_completion(&sg.comp);
+	usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
+			alauda_complete, NULL);
+	usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize,
+			alauda_complete, NULL);
+	usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16,
+			alauda_complete, &sg.comp);
+
+	mutex_lock(&al->card_mutex);
+	for (i=0; i<3; i++) {
+		err = usb_submit_urb(sg.urb[i], GFP_NOIO);
+		if (err)
+			goto cancel;
+	}
+	if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
+		err = -ETIMEDOUT;
+cancel:
+		for (i=0; i<3; i++) {
+			usb_kill_urb(sg.urb[i]);
+		}
+	}
+	mutex_unlock(&al->card_mutex);
+
+out:
+	usb_free_urb(sg.urb[0]);
+	usb_free_urb(sg.urb[1]);
+	usb_free_urb(sg.urb[2]);
+	return err;
+}
+
+static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs)
+{
+	struct alauda_sg_request sg;
+	struct alauda *al = mtd->priv;
+	u32 pba = ofs >> al->card->blockshift;
+	u8 command[] = {
+		ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba),
+		PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port
+	};
+	u8 buf[2];
+	int i, err;
+
+	for (i=0; i<2; i++)
+		sg.urb[i] = NULL;
+
+	err = -ENOMEM;
+	for (i=0; i<2; i++) {
+		sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
+		if (!sg.urb[i])
+			goto out;
+	}
+	init_completion(&sg.comp);
+	usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
+			alauda_complete, NULL);
+	usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2,
+			alauda_complete, &sg.comp);
+
+	mutex_lock(&al->card_mutex);
+	for (i=0; i<2; i++) {
+		err = usb_submit_urb(sg.urb[i], GFP_NOIO);
+		if (err)
+			goto cancel;
+	}
+	if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
+		err = -ETIMEDOUT;
+cancel:
+		for (i=0; i<2; i++) {
+			usb_kill_urb(sg.urb[i]);
+		}
+	}
+	mutex_unlock(&al->card_mutex);
+
+out:
+	usb_free_urb(sg.urb[0]);
+	usb_free_urb(sg.urb[1]);
+	return err;
+}
+
+static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob)
+{
+	static u8 ignore_buf[512]; /* write only */
+
+	return __alauda_read_page(mtd, from, ignore_buf, oob);
+}
+
+static int popcount8(u8 c)
+{
+	int ret = 0;
+
+	for ( ; c; c>>=1)
+		ret += c & 1;
+	return ret;
+}
+
+static int alauda_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+	u8 oob[16];
+	int err;
+
+	err = alauda_read_oob(mtd, ofs, oob);
+	if (err)
+		return err;
+
+	/* A block is marked bad if two or more bits are zero */
+	return popcount8(oob[5]) >= 7 ? 0 : 1;
+}
+
+static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char *buf)
+{
+	struct alauda *al = mtd->priv;
+	void *bounce_buf;
+	int err, corrected=0, uncorrected=0;
+
+	bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL);
+	if (!bounce_buf)
+		return -ENOMEM;
+
+	*retlen = len;
+	while (len) {
+		u8 oob[16];
+		size_t byte = from & al->bytemask;
+		size_t cplen = min(len, mtd->writesize - byte);
+
+		err = alauda_read_page(mtd, from, bounce_buf, oob,
+				&corrected, &uncorrected);
+		if (err)
+			goto out;
+
+		memcpy(buf, bounce_buf + byte, cplen);
+		buf += cplen;
+		from += cplen;
+		len -= cplen;
+	}
+	err = 0;
+	if (corrected)
+		err = -EUCLEAN;
+	if (uncorrected)
+		err = -EBADMSG;
+out:
+	kfree(bounce_buf);
+	return err;
+}
+
+static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char *buf)
+{
+	struct alauda *al = mtd->priv;
+	int err, corrected=0, uncorrected=0;
+
+	if ((from & al->bytemask) || (len & al->bytemask))
+		return alauda_bounce_read(mtd, from, len, retlen, buf);
+
+	*retlen = len;
+	while (len) {
+		u8 oob[16];
+
+		err = alauda_read_page(mtd, from, buf, oob,
+				&corrected, &uncorrected);
+		if (err)
+			return err;
+
+		buf += mtd->writesize;
+		from += mtd->writesize;
+		len -= mtd->writesize;
+	}
+	err = 0;
+	if (corrected)
+		err = -EUCLEAN;
+	if (uncorrected)
+		err = -EBADMSG;
+	return err;
+}
+
+static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len,
+		size_t *retlen, const u_char *buf)
+{
+	struct alauda *al = mtd->priv;
+	int err;
+
+	if ((to & al->bytemask) || (len & al->bytemask))
+		return -EINVAL;
+
+	*retlen = len;
+	while (len) {
+		u32 page = (to >> al->card->pageshift) & al->pagemask;
+		u8 oob[16] = {	'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+		/* don't write to bad blocks */
+		if (page == 0) {
+			err = alauda_isbad(mtd, to);
+			if (err) {
+				return -EIO;
+			}
+		}
+		nand_calculate_ecc(mtd, buf, &oob[13]);
+		nand_calculate_ecc(mtd, buf+256, &oob[8]);
+
+		err = alauda_write_page(mtd, to, (void*)buf, oob);
+		if (err)
+			return err;
+
+		buf += mtd->writesize;
+		to += mtd->writesize;
+		len -= mtd->writesize;
+	}
+	return 0;
+}
+
+static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	struct alauda *al = mtd->priv;
+	u32 ofs = instr->addr;
+	u32 len = instr->len;
+	int err;
+
+	if ((ofs & al->blockmask) || (len & al->blockmask))
+		return -EINVAL;
+
+	while (len) {
+		/* don't erase bad blocks */
+		err = alauda_isbad(mtd, ofs);
+		if (err > 0)
+			err = -EIO;
+		if (err < 0)
+			return err;
+
+		err = alauda_erase_block(mtd, ofs);
+		if (err < 0)
+			return err;
+
+		ofs += mtd->erasesize;
+		len -= mtd->erasesize;
+	}
+	return 0;
+}
+
+static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	int err;
+
+	err = __alauda_erase(mtd, instr);
+	instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
+	mtd_erase_callback(instr);
+	return err;
+}
+
+static int alauda_init_media(struct alauda *al)
+{
+	u8 buf[4], *b0=buf, *b1=buf+1;
+	struct alauda_card *card;
+	struct mtd_info *mtd;
+	int err;
+
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
+	if (!mtd)
+		return -ENOMEM;
+
+	for (;;) {
+		err = alauda_get_media_status(al, buf);
+		if (err < 0)
+			goto error;
+		if (*b0 & 0x10)
+			break;
+		msleep(20);
+	}
+
+	err = alauda_ack_media(al);
+	if (err)
+		goto error;
+
+	msleep(10);
+
+	err = alauda_get_media_status(al, buf);
+	if (err < 0)
+		goto error;
+
+	if (*b0 != 0x14) {
+		/* media not ready */
+		err = -EIO;
+		goto error;
+	}
+	err = alauda_get_media_signatures(al, buf);
+	if (err < 0)
+		goto error;
+
+	card = get_card(*b1);
+	if (!card) {
+		printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1);
+		err = -EIO;
+		goto error;
+	}
+	printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n",
+			1<<card->pageshift, 1<<card->blockshift,
+			1<<(card->chipshift-20));
+	al->card = card;
+	al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1;
+	al->bytemask = (1 << card->pageshift) - 1;
+	al->blockmask = (1 << card->blockshift) - 1;
+
+	mtd->name = "alauda";
+	mtd->size = 1<<card->chipshift;
+	mtd->erasesize = 1<<card->blockshift;
+	mtd->writesize = 1<<card->pageshift;
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->read = alauda_read;
+	mtd->write = alauda_write;
+	mtd->erase = alauda_erase;
+	mtd->block_isbad = alauda_isbad;
+	mtd->priv = al;
+	mtd->owner = THIS_MODULE;
+
+	err = add_mtd_device(mtd);
+	if (err) {
+		err = -ENFILE;
+		goto error;
+	}
+
+	al->mtd = mtd;
+	alauda_reset(al); /* no clue whether this is necessary */
+	return 0;
+error:
+	kfree(mtd);
+	return err;
+}
+
+static int alauda_check_media(struct alauda *al)
+{
+	u8 buf[2], *b0 = buf, *b1 = buf+1;
+	int err;
+
+	err = alauda_get_media_status(al, buf);
+	if (err < 0)
+		return err;
+
+	if ((*b1 & 0x01) == 0) {
+		/* door open */
+		return -EIO;
+	}
+	if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) {
+		/* no media ? */
+		return -EIO;
+	}
+	if (*b0 & 0x08) {
+		/* media change ? */
+		return alauda_init_media(al);
+	}
+	return 0;
+}
+
+static int alauda_probe(struct usb_interface *interface,
+		const struct usb_device_id *id)
+{
+	struct alauda *al;
+	struct usb_host_interface *iface;
+	struct usb_endpoint_descriptor *ep,
+			*ep_in=NULL, *ep_out=NULL, *ep_wr=NULL;
+	int i, err = -ENOMEM;
+
+	al = kzalloc(2*sizeof(*al), GFP_KERNEL);
+	if (!al)
+		goto error;
+
+	kref_init(&al->kref);
+	usb_set_intfdata(interface, al);
+
+	al->dev = usb_get_dev(interface_to_usbdev(interface));
+	al->interface = interface;
+
+	iface = interface->cur_altsetting;
+	for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+		ep = &iface->endpoint[i].desc;
+
+		if (usb_endpoint_is_bulk_in(ep)) {
+			ep_in = ep;
+		} else if (usb_endpoint_is_bulk_out(ep)) {
+			if (i==0)
+				ep_wr = ep;
+			else
+				ep_out = ep;
+		}
+	}
+	err = -EIO;
+	if (!ep_wr || !ep_in || !ep_out)
+		goto error;
+
+	al->write_out = usb_sndbulkpipe(al->dev,
+			ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	al->bulk_in = usb_rcvbulkpipe(al->dev,
+			ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	al->bulk_out = usb_sndbulkpipe(al->dev,
+			ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	/* second device is identical up to now */
+	memcpy(al+1, al, sizeof(*al));
+
+	mutex_init(&al[0].card_mutex);
+	mutex_init(&al[1].card_mutex);
+
+	al[0].port = ALAUDA_PORT_XD;
+	al[1].port = ALAUDA_PORT_SM;
+
+	info("alauda probed");
+	alauda_check_media(al);
+	alauda_check_media(al+1);
+
+	return 0;
+
+error:
+	if (al)
+		kref_put(&al->kref, alauda_delete);
+	return err;
+}
+
+static void alauda_disconnect(struct usb_interface *interface)
+{
+	struct alauda *al;
+
+	al = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	/* FIXME: prevent more I/O from starting */
+
+	/* decrement our usage count */
+	if (al)
+		kref_put(&al->kref, alauda_delete);
+
+	info("alauda gone");
+}
+
+static struct usb_driver alauda_driver = {
+	.name =		"alauda",
+	.probe =	alauda_probe,
+	.disconnect =	alauda_disconnect,
+	.id_table =	alauda_table,
+};
+
+static int __init alauda_init(void)
+{
+	return usb_register(&alauda_driver);
+}
+
+static void __exit alauda_exit(void)
+{
+	usb_deregister(&alauda_driver);
+}
+
+module_init(alauda_init);
+module_exit(alauda_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
new file mode 100644
index 0000000..1657ecd
--- /dev/null
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -0,0 +1,788 @@
+/* linux/drivers/mtd/nand/bf5xx_nand.c
+ *
+ * Copyright 2006-2007 Analog Devices Inc.
+ *	http://blackfin.uclinux.org/
+ *	Bryan Wu <bryan.wu@analog.com>
+ *
+ * Blackfin BF5xx on-chip NAND flash controler driver
+ *
+ * Derived from drivers/mtd/nand/s3c2410.c
+ * Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk>
+ *
+ * Derived from drivers/mtd/nand/cafe.c
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Changelog:
+ *	12-Jun-2007  Bryan Wu:  Initial version
+ *	18-Jul-2007  Bryan Wu:
+ *		- ECC_HW and ECC_SW supported
+ *		- DMA supported in ECC_HW
+ *		- YAFFS tested as rootfs in both ECC_HW and ECC_SW
+ *
+ * TODO:
+ * 	Enable JFFS2 over NAND as rootfs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/blackfin.h>
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+#include <asm/nand.h>
+#include <asm/portmux.h>
+
+#define DRV_NAME	"bf5xx-nand"
+#define DRV_VERSION	"1.2"
+#define DRV_AUTHOR	"Bryan Wu <bryan.wu@analog.com>"
+#define DRV_DESC	"BF5xx on-chip NAND FLash Controller Driver"
+
+#ifdef CONFIG_MTD_NAND_BF5XX_HWECC
+static int hardware_ecc = 1;
+#else
+static int hardware_ecc;
+#endif
+
+static unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, 0};
+
+/*
+ * Data structures for bf5xx nand flash controller driver
+ */
+
+/* bf5xx nand info */
+struct bf5xx_nand_info {
+	/* mtd info */
+	struct nand_hw_control		controller;
+	struct mtd_info			mtd;
+	struct nand_chip		chip;
+
+	/* platform info */
+	struct bf5xx_nand_platform	*platform;
+
+	/* device info */
+	struct device			*device;
+
+	/* DMA stuff */
+	struct completion		dma_completion;
+};
+
+/*
+ * Conversion functions
+ */
+static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd)
+{
+	return container_of(mtd, struct bf5xx_nand_info, mtd);
+}
+
+static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
+{
+	return platform_get_drvdata(pdev);
+}
+
+static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
+{
+	return pdev->dev.platform_data;
+}
+
+/*
+ * struct nand_chip interface function pointers
+ */
+
+/*
+ * bf5xx_nand_hwcontrol
+ *
+ * Issue command and address cycles to the chip
+ */
+static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+				   unsigned int ctrl)
+{
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	while (bfin_read_NFC_STAT() & WB_FULL)
+		cpu_relax();
+
+	if (ctrl & NAND_CLE)
+		bfin_write_NFC_CMD(cmd);
+	else
+		bfin_write_NFC_ADDR(cmd);
+	SSYNC();
+}
+
+/*
+ * bf5xx_nand_devready()
+ *
+ * returns 0 if the nand is busy, 1 if it is ready
+ */
+static int bf5xx_nand_devready(struct mtd_info *mtd)
+{
+	unsigned short val = bfin_read_NFC_IRQSTAT();
+
+	if ((val & NBUSYIRQ) == NBUSYIRQ)
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * ECC functions
+ * These allow the bf5xx to use the controller's ECC
+ * generator block to ECC the data as it passes through
+ */
+
+/*
+ * ECC error correction function
+ */
+static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
+					u_char *read_ecc, u_char *calc_ecc)
+{
+	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+	u32 syndrome[5];
+	u32 calced, stored;
+	int i;
+	unsigned short failing_bit, failing_byte;
+	u_char data;
+
+	calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
+	stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
+
+	syndrome[0] = (calced ^ stored);
+
+	/*
+	 * syndrome 0: all zero
+	 * No error in data
+	 * No action
+	 */
+	if (!syndrome[0] || !calced || !stored)
+		return 0;
+
+	/*
+	 * sysdrome 0: only one bit is one
+	 * ECC data was incorrect
+	 * No action
+	 */
+	if (hweight32(syndrome[0]) == 1) {
+		dev_err(info->device, "ECC data was incorrect!\n");
+		return 1;
+	}
+
+	syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
+	syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
+	syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
+	syndrome[4] = syndrome[2] ^ syndrome[3];
+
+	for (i = 0; i < 5; i++)
+		dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]);
+
+	dev_info(info->device,
+		"calced[0x%08x], stored[0x%08x]\n",
+		calced, stored);
+
+	/*
+	 * sysdrome 0: exactly 11 bits are one, each parity
+	 * and parity' pair is 1 & 0 or 0 & 1.
+	 * 1-bit correctable error
+	 * Correct the error
+	 */
+	if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
+		dev_info(info->device,
+			"1-bit correctable error, correct it.\n");
+		dev_info(info->device,
+			"syndrome[1] 0x%08x\n", syndrome[1]);
+
+		failing_bit = syndrome[1] & 0x7;
+		failing_byte = syndrome[1] >> 0x3;
+		data = *(dat + failing_byte);
+		data = data ^ (0x1 << failing_bit);
+		*(dat + failing_byte) = data;
+
+		return 0;
+	}
+
+	/*
+	 * sysdrome 0: random data
+	 * More than 1-bit error, non-correctable error
+	 * Discard data, mark bad block
+	 */
+	dev_err(info->device,
+		"More than 1-bit error, non-correctable error.\n");
+	dev_err(info->device,
+		"Please discard data, mark bad block\n");
+
+	return 1;
+}
+
+static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+					u_char *read_ecc, u_char *calc_ecc)
+{
+	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+	struct bf5xx_nand_platform *plat = info->platform;
+	unsigned short page_size = (plat->page_size ? 512 : 256);
+	int ret;
+
+	ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
+
+	/* If page size is 512, correct second 256 bytes */
+	if (page_size == 512) {
+		dat += 256;
+		read_ecc += 8;
+		calc_ecc += 8;
+		ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
+	}
+
+	return ret;
+}
+
+static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	return;
+}
+
+static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
+		const u_char *dat, u_char *ecc_code)
+{
+	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+	struct bf5xx_nand_platform *plat = info->platform;
+	u16 page_size = (plat->page_size ? 512 : 256);
+	u16 ecc0, ecc1;
+	u32 code[2];
+	u8 *p;
+	int bytes = 3, i;
+
+	/* first 4 bytes ECC code for 256 page size */
+	ecc0 = bfin_read_NFC_ECC0();
+	ecc1 = bfin_read_NFC_ECC1();
+
+	code[0] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11);
+
+	dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);
+
+	/* second 4 bytes ECC code for 512 page size */
+	if (page_size == 512) {
+		ecc0 = bfin_read_NFC_ECC2();
+		ecc1 = bfin_read_NFC_ECC3();
+		code[1] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11);
+		bytes = 6;
+		dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]);
+	}
+
+	p = (u8 *)code;
+	for (i = 0; i < bytes; i++)
+		ecc_code[i] = p[i];
+
+	return 0;
+}
+
+/*
+ * PIO mode for buffer writing and reading
+ */
+static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	int i;
+	unsigned short val;
+
+	/*
+	 * Data reads are requested by first writing to NFC_DATA_RD
+	 * and then reading back from NFC_READ.
+	 */
+	for (i = 0; i < len; i++) {
+		while (bfin_read_NFC_STAT() & WB_FULL)
+			cpu_relax();
+
+		/* Contents do not matter */
+		bfin_write_NFC_DATA_RD(0x0000);
+		SSYNC();
+
+		while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY)
+			cpu_relax();
+
+		buf[i] = bfin_read_NFC_READ();
+
+		val = bfin_read_NFC_IRQSTAT();
+		val |= RD_RDY;
+		bfin_write_NFC_IRQSTAT(val);
+		SSYNC();
+	}
+}
+
+static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd)
+{
+	uint8_t val;
+
+	bf5xx_nand_read_buf(mtd, &val, 1);
+
+	return val;
+}
+
+static void bf5xx_nand_write_buf(struct mtd_info *mtd,
+				const uint8_t *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		while (bfin_read_NFC_STAT() & WB_FULL)
+			cpu_relax();
+
+		bfin_write_NFC_DATA_WR(buf[i]);
+		SSYNC();
+	}
+}
+
+static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	int i;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	/*
+	 * Data reads are requested by first writing to NFC_DATA_RD
+	 * and then reading back from NFC_READ.
+	 */
+	bfin_write_NFC_DATA_RD(0x5555);
+
+	SSYNC();
+
+	for (i = 0; i < len; i++)
+		p[i] = bfin_read_NFC_READ();
+}
+
+static void bf5xx_nand_write_buf16(struct mtd_info *mtd,
+				const uint8_t *buf, int len)
+{
+	int i;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i = 0; i < len; i++)
+		bfin_write_NFC_DATA_WR(p[i]);
+
+	SSYNC();
+}
+
+/*
+ * DMA functions for buffer writing and reading
+ */
+static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
+{
+	struct bf5xx_nand_info *info = dev_id;
+
+	clear_dma_irqstat(CH_NFC);
+	disable_dma(CH_NFC);
+	complete(&info->dma_completion);
+
+	return IRQ_HANDLED;
+}
+
+static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
+				uint8_t *buf, int is_read)
+{
+	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+	struct bf5xx_nand_platform *plat = info->platform;
+	unsigned short page_size = (plat->page_size ? 512 : 256);
+	unsigned short val;
+
+	dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n",
+			mtd, buf, is_read);
+
+	/*
+	 * Before starting a dma transfer, be sure to invalidate/flush
+	 * the cache over the address range of your DMA buffer to
+	 * prevent cache coherency problems. Otherwise very subtle bugs
+	 * can be introduced to your driver.
+	 */
+	if (is_read)
+		invalidate_dcache_range((unsigned int)buf,
+				(unsigned int)(buf + page_size));
+	else
+		flush_dcache_range((unsigned int)buf,
+				(unsigned int)(buf + page_size));
+
+	/*
+	 * This register must be written before each page is
+	 * transferred to generate the correct ECC register
+	 * values.
+	 */
+	bfin_write_NFC_RST(0x1);
+	SSYNC();
+
+	disable_dma(CH_NFC);
+	clear_dma_irqstat(CH_NFC);
+
+	/* setup DMA register with Blackfin DMA API */
+	set_dma_config(CH_NFC, 0x0);
+	set_dma_start_addr(CH_NFC, (unsigned long) buf);
+	set_dma_x_count(CH_NFC, (page_size >> 2));
+	set_dma_x_modify(CH_NFC, 4);
+
+	/* setup write or read operation */
+	val = DI_EN | WDSIZE_32;
+	if (is_read)
+		val |= WNR;
+	set_dma_config(CH_NFC, val);
+	enable_dma(CH_NFC);
+
+	/* Start PAGE read/write operation */
+	if (is_read)
+		bfin_write_NFC_PGCTL(0x1);
+	else
+		bfin_write_NFC_PGCTL(0x2);
+	wait_for_completion(&info->dma_completion);
+
+	return 0;
+}
+
+static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
+					uint8_t *buf, int len)
+{
+	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+	struct bf5xx_nand_platform *plat = info->platform;
+	unsigned short page_size = (plat->page_size ? 512 : 256);
+
+	dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len);
+
+	if (len == page_size)
+		bf5xx_nand_dma_rw(mtd, buf, 1);
+	else
+		bf5xx_nand_read_buf(mtd, buf, len);
+}
+
+static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
+				const uint8_t *buf, int len)
+{
+	struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+	struct bf5xx_nand_platform *plat = info->platform;
+	unsigned short page_size = (plat->page_size ? 512 : 256);
+
+	dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len);
+
+	if (len == page_size)
+		bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0);
+	else
+		bf5xx_nand_write_buf(mtd, buf, len);
+}
+
+/*
+ * System initialization functions
+ */
+
+static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
+{
+	int ret;
+	unsigned short val;
+
+	/* Do not use dma */
+	if (!hardware_ecc)
+		return 0;
+
+	init_completion(&info->dma_completion);
+
+	/* Setup DMAC1 channel mux for NFC which shared with SDH */
+	val = bfin_read_DMAC1_PERIMUX();
+	val &= 0xFFFE;
+	bfin_write_DMAC1_PERIMUX(val);
+	SSYNC();
+
+	/* Request NFC DMA channel */
+	ret = request_dma(CH_NFC, "BF5XX NFC driver");
+	if (ret < 0) {
+		dev_err(info->device, " unable to get DMA channel\n");
+		return ret;
+	}
+
+	set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info);
+
+	/* Turn off the DMA channel first */
+	disable_dma(CH_NFC);
+	return 0;
+}
+
+/*
+ * BF5XX NFC hardware initialization
+ *  - pin mux setup
+ *  - clear interrupt status
+ */
+static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
+{
+	int err = 0;
+	unsigned short val;
+	struct bf5xx_nand_platform *plat = info->platform;
+
+	/* setup NFC_CTL register */
+	dev_info(info->device,
+		"page_size=%d, data_width=%d, wr_dly=%d, rd_dly=%d\n",
+		(plat->page_size ? 512 : 256),
+		(plat->data_width ? 16 : 8),
+		plat->wr_dly, plat->rd_dly);
+
+	val = (plat->page_size << NFC_PG_SIZE_OFFSET) |
+		(plat->data_width << NFC_NWIDTH_OFFSET) |
+		(plat->rd_dly << NFC_RDDLY_OFFSET) |
+		(plat->rd_dly << NFC_WRDLY_OFFSET);
+	dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val);
+
+	bfin_write_NFC_CTL(val);
+	SSYNC();
+
+	/* clear interrupt status */
+	bfin_write_NFC_IRQMASK(0x0);
+	SSYNC();
+	val = bfin_read_NFC_IRQSTAT();
+	bfin_write_NFC_IRQSTAT(val);
+	SSYNC();
+
+	if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) {
+		printk(KERN_ERR DRV_NAME
+		": Requesting Peripherals failed\n");
+		return -EFAULT;
+	}
+
+	/* DMA initialization  */
+	if (bf5xx_nand_dma_init(info))
+		err = -ENXIO;
+
+	return err;
+}
+
+/*
+ * Device management interface
+ */
+static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
+{
+	struct mtd_info *mtd = &info->mtd;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	struct mtd_partition *parts = info->platform->partitions;
+	int nr = info->platform->nr_partitions;
+
+	return add_mtd_partitions(mtd, parts, nr);
+#else
+	return add_mtd_device(mtd);
+#endif
+}
+
+static int bf5xx_nand_remove(struct platform_device *pdev)
+{
+	struct bf5xx_nand_info *info = to_nand_info(pdev);
+	struct mtd_info *mtd = NULL;
+
+	platform_set_drvdata(pdev, NULL);
+
+	/* first thing we need to do is release all our mtds
+	 * and their partitions, then go through freeing the
+	 * resources used
+	 */
+	mtd = &info->mtd;
+	if (mtd) {
+		nand_release(mtd);
+		kfree(mtd);
+	}
+
+	peripheral_free_list(bfin_nfc_pin_req);
+
+	/* free the common resources */
+	kfree(info);
+
+	return 0;
+}
+
+/*
+ * bf5xx_nand_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code checks to see if
+ * it can allocate all necessary resources then calls the
+ * nand layer to look for devices
+ */
+static int bf5xx_nand_probe(struct platform_device *pdev)
+{
+	struct bf5xx_nand_platform *plat = to_nand_plat(pdev);
+	struct bf5xx_nand_info *info = NULL;
+	struct nand_chip *chip = NULL;
+	struct mtd_info *mtd = NULL;
+	int err = 0;
+
+	dev_dbg(&pdev->dev, "(%p)\n", pdev);
+
+	if (!plat) {
+		dev_err(&pdev->dev, "no platform specific information\n");
+		goto exit_error;
+	}
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (info == NULL) {
+		dev_err(&pdev->dev, "no memory for flash info\n");
+		err = -ENOMEM;
+		goto exit_error;
+	}
+
+	platform_set_drvdata(pdev, info);
+
+	spin_lock_init(&info->controller.lock);
+	init_waitqueue_head(&info->controller.wq);
+
+	info->device     = &pdev->dev;
+	info->platform   = plat;
+
+	/* initialise chip data struct */
+	chip = &info->chip;
+
+	if (plat->data_width)
+		chip->options |= NAND_BUSWIDTH_16;
+
+	chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN;
+
+	chip->read_buf = (plat->data_width) ?
+		bf5xx_nand_read_buf16 : bf5xx_nand_read_buf;
+	chip->write_buf = (plat->data_width) ?
+		bf5xx_nand_write_buf16 : bf5xx_nand_write_buf;
+
+	chip->read_byte    = bf5xx_nand_read_byte;
+
+	chip->cmd_ctrl     = bf5xx_nand_hwcontrol;
+	chip->dev_ready    = bf5xx_nand_devready;
+
+	chip->priv	   = &info->mtd;
+	chip->controller   = &info->controller;
+
+	chip->IO_ADDR_R    = (void __iomem *) NFC_READ;
+	chip->IO_ADDR_W    = (void __iomem *) NFC_DATA_WR;
+
+	chip->chip_delay   = 0;
+
+	/* initialise mtd info data struct */
+	mtd 		= &info->mtd;
+	mtd->priv	= chip;
+	mtd->owner	= THIS_MODULE;
+
+	/* initialise the hardware */
+	err = bf5xx_nand_hw_init(info);
+	if (err != 0)
+		goto exit_error;
+
+	/* setup hardware ECC data struct */
+	if (hardware_ecc) {
+		if (plat->page_size == NFC_PG_SIZE_256) {
+			chip->ecc.bytes = 3;
+			chip->ecc.size = 256;
+		} else if (plat->page_size == NFC_PG_SIZE_512) {
+			chip->ecc.bytes = 6;
+			chip->ecc.size = 512;
+		}
+
+		chip->read_buf      = bf5xx_nand_dma_read_buf;
+		chip->write_buf     = bf5xx_nand_dma_write_buf;
+		chip->ecc.calculate = bf5xx_nand_calculate_ecc;
+		chip->ecc.correct   = bf5xx_nand_correct_data;
+		chip->ecc.mode	    = NAND_ECC_HW;
+		chip->ecc.hwctl	    = bf5xx_nand_enable_hwecc;
+	} else {
+		chip->ecc.mode	    = NAND_ECC_SOFT;
+	}
+
+	/* scan hardware nand chip and setup mtd info data struct */
+	if (nand_scan(mtd, 1)) {
+		err = -ENXIO;
+		goto exit_error;
+	}
+
+	/* add NAND partition */
+	bf5xx_nand_add_partition(info);
+
+	dev_dbg(&pdev->dev, "initialised ok\n");
+	return 0;
+
+exit_error:
+	bf5xx_nand_remove(pdev);
+
+	if (err == 0)
+		err = -EINVAL;
+	return err;
+}
+
+/* PM Support */
+#ifdef CONFIG_PM
+
+static int bf5xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
+{
+	struct bf5xx_nand_info *info = platform_get_drvdata(dev);
+
+	return 0;
+}
+
+static int bf5xx_nand_resume(struct platform_device *dev)
+{
+	struct bf5xx_nand_info *info = platform_get_drvdata(dev);
+
+	if (info)
+		bf5xx_nand_hw_init(info);
+
+	return 0;
+}
+
+#else
+#define bf5xx_nand_suspend NULL
+#define bf5xx_nand_resume NULL
+#endif
+
+/* driver device registration */
+static struct platform_driver bf5xx_nand_driver = {
+	.probe		= bf5xx_nand_probe,
+	.remove		= bf5xx_nand_remove,
+	.suspend	= bf5xx_nand_suspend,
+	.resume		= bf5xx_nand_resume,
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init bf5xx_nand_init(void)
+{
+	printk(KERN_INFO "%s, Version %s (c) 2007 Analog Devices, Inc.\n",
+		DRV_DESC, DRV_VERSION);
+
+	return platform_driver_register(&bf5xx_nand_driver);
+}
+
+static void __exit bf5xx_nand_exit(void)
+{
+	platform_driver_unregister(&bf5xx_nand_driver);
+}
+
+module_init(bf5xx_nand_init);
+module_exit(bf5xx_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_DESCRIPTION(DRV_DESC);
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index 6f32a35..e2832d0 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -623,6 +623,11 @@
 	uint32_t ctrl;
 	int err = 0;
 
+	/* Very old versions shared the same PCI ident for all three
+	   functions on the chip. Verify the class too... */
+	if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH)
+		return -ENODEV;
+
 	err = pci_enable_device(pdev);
 	if (err)
 		return err;
@@ -816,21 +821,57 @@
 }
 
 static struct pci_device_id cafe_nand_tbl[] = {
-	{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 },
-	{ 0, }
+	{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID },
+	{ }
 };
 
 MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
 
+static int cafe_nand_resume(struct pci_dev *pdev)
+{
+	uint32_t ctrl;
+	struct mtd_info *mtd = pci_get_drvdata(pdev);
+	struct cafe_priv *cafe = mtd->priv;
+
+       /* Start off by resetting the NAND controller completely */
+	cafe_writel(cafe, 1, NAND_RESET);
+	cafe_writel(cafe, 0, NAND_RESET);
+	cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
+
+	/* Restore timing configuration */
+	cafe_writel(cafe, timing[0], NAND_TIMING1);
+	cafe_writel(cafe, timing[1], NAND_TIMING2);
+	cafe_writel(cafe, timing[2], NAND_TIMING3);
+
+        /* Disable master reset, enable NAND clock */
+	ctrl = cafe_readl(cafe, GLOBAL_CTRL);
+	ctrl &= 0xffffeff0;
+	ctrl |= 0x00007000;
+	cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
+	cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
+	cafe_writel(cafe, 0, NAND_DMA_CTRL);
+	cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
+	cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
+
+	/* Set up DMA address */
+	cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
+	if (sizeof(cafe->dmaaddr) > 4)
+	/* Shift in two parts to shut the compiler up */
+		cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1);
+	else
+		cafe_writel(cafe, 0, NAND_DMA_ADDR1);
+
+	/* Enable NAND IRQ in global IRQ mask register */
+	cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+	return 0;
+}
+
 static struct pci_driver cafe_nand_pci_driver = {
 	.name = "CAFÉ NAND",
 	.id_table = cafe_nand_tbl,
 	.probe = cafe_nand_probe,
 	.remove = __devexit_p(cafe_nand_remove),
-#ifdef CONFIG_PMx
-	.suspend = cafe_nand_suspend,
 	.resume = cafe_nand_resume,
-#endif
 };
 
 static int cafe_nand_init(void)
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index e96259f..ab9f5c5 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -56,8 +56,6 @@
 #endif /*  CONFIG_MTD_DOCPROBE_HIGH */
 #elif defined(__PPC__)
 	0xe4000000,
-#elif defined(CONFIG_MOMENCO_OCELOT_G)
-	0xff000000,
 #else
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #endif
diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c
index 7e9afc4..bed8729 100644
--- a/drivers/mtd/nand/excite_nandflash.c
+++ b/drivers/mtd/nand/excite_nandflash.c
@@ -27,7 +27,6 @@
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/err.h>
-#include <linux/kernel.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 24ac677..b4e0e77 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -7,7 +7,7 @@
  *   Basic support for AG-AND chips is provided.
  *
  *	Additional technical information is available on
- *	http://www.linux-mtd.infradead.org/tech/nand.html
+ *	http://www.linux-mtd.infradead.org/doc/nand.html
  *
  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *		  2002-2006 Thomas Gleixner (tglx@linutronix.de)
@@ -2069,13 +2069,14 @@
  erase_exit:
 
 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-	/* Do call back function */
-	if (!ret)
-		mtd_erase_callback(instr);
 
 	/* Deselect and wake up anyone waiting on the device */
 	nand_release_device(mtd);
 
+	/* Do call back function */
+	if (!ret)
+		mtd_erase_callback(instr);
+
 	/*
 	 * If BBT requires refresh and erase was successful, rewrite any
 	 * selected bad block tables
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 2fc674a..a3e3ab0 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -141,6 +141,7 @@
 	{NAND_MFR_STMICRO, "ST Micro"},
 	{NAND_MFR_HYNIX, "Hynix"},
 	{NAND_MFR_MICRON, "Micron"},
+	{NAND_MFR_AMD, "AMD"},
 	{0x0, "Unknown"}
 };
 
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index 205df0f..a757480 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -1272,7 +1272,13 @@
 	mypage = NS_GET_PAGE(ns);
 	if (mypage->byte == NULL) {
 		NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
-		mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+		/*
+		 * We allocate memory with GFP_NOFS because a flash FS may
+		 * utilize this. If it is holding an FS lock, then gets here,
+		 * then kmalloc runs writeback which goes to the FS again
+		 * and deadlocks. This was seen in practice.
+		 */
+		mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS);
 		if (mypage->byte == NULL) {
 			NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
 			return -1;
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index fd7a8d5..1c0e89f 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -24,7 +24,11 @@
 #include <linux/platform_device.h>
 
 #include <asm/io.h>
+#ifdef CONFIG_40x
+#include <asm/ibm405.h>
+#else
 #include <asm/ibm44x.h>
+#endif
 
 struct ndfc_nand_mtd {
 	struct mtd_info			mtd;
@@ -230,7 +234,11 @@
 	struct ndfc_controller *ndfc = &ndfc_ctrl;
 	unsigned long long phys = settings->ndfc_erpn | res->start;
 
+#ifndef CONFIG_PHYS_64BIT
+	ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1);
+#else
 	ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
+#endif
 	if (!ndfc->ndfcbase) {
 		printk(KERN_ERR "NDFC: ioremap failed\n");
 		return -EIO;
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 5fac4c4..b79a9cf 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -60,8 +60,8 @@
 
 #include <asm/io.h>
 
-#include <asm/arch/regs-nand.h>
-#include <asm/arch/nand.h>
+#include <asm/plat-s3c/regs-nand.h>
+#include <asm/plat-s3c/nand.h>
 
 #ifdef CONFIG_MTD_NAND_S3C2410_HWECC
 static int hardware_ecc = 1;
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index c257d39..cb41cbc 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -40,4 +40,27 @@
 
 	  OTP block is fully-guaranteed to be a valid block.
 
+config MTD_ONENAND_2X_PROGRAM
+	bool "OneNAND 2X program support"
+	help
+	  The 2X Program is an extension of Program Operation.
+	  Since the device is equipped with two DataRAMs, and two-plane NAND
+	  Flash memory array, these two component enables simultaneous program
+	  of 4KiB. Plane1 has only even blocks such as block0, block2, block4
+	  while Plane2 has only odd blocks such as block1, block3, block5.
+	  So MTD regards it as 4KiB page size and 256KiB block size
+
+	  Now the following chips support it. (KFXXX16Q2M)
+	    Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M,
+	    Mux:   KFM2G16Q2M, KFN4G16Q2M,
+
+	  And more recent chips
+
+config MTD_ONENAND_SIM
+	tristate "OneNAND simulator support"
+	depends on MTD_PARTITIONS
+	help
+	  The simulator may simulate various OneNAND flash chips for the
+	  OneNAND MTD layer.
+
 endif # MTD_ONENAND
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile
index 269cfe4..4d2eacf 100644
--- a/drivers/mtd/onenand/Makefile
+++ b/drivers/mtd/onenand/Makefile
@@ -8,4 +8,7 @@
 # Board specific.
 obj-$(CONFIG_MTD_ONENAND_GENERIC)	+= generic.o
 
+# Simulator
+obj-$(CONFIG_MTD_ONENAND_SIM)		+= onenand_sim.o
+
 onenand-objs = onenand_base.o onenand_bbt.o
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 0537fac..b2c40f6 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -206,6 +206,15 @@
 	default:
 		block = (int) (addr >> this->erase_shift);
 		page = (int) (addr >> this->page_shift);
+
+		if (ONENAND_IS_2PLANE(this)) {
+			/* Make the even block number */
+			block &= ~1;
+			/* Is it the odd plane? */
+			if (addr & this->writesize)
+				block++;
+			page >>= 1;
+		}
 		page &= this->page_mask;
 		break;
 	}
@@ -216,8 +225,12 @@
 		value = onenand_bufferram_address(this, block);
 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 
-		/* Switch to the next data buffer */
-		ONENAND_SET_NEXT_BUFFERRAM(this);
+		if (ONENAND_IS_2PLANE(this))
+			/* It is always BufferRAM0 */
+			ONENAND_SET_BUFFERRAM0(this);
+		else
+			/* Switch to the next data buffer */
+			ONENAND_SET_NEXT_BUFFERRAM(this);
 
 		return 0;
 	}
@@ -247,6 +260,8 @@
 			break;
 
 		default:
+			if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+				cmd = ONENAND_CMD_2X_PROG;
 			dataram = ONENAND_CURRENT_BUFFERRAM(this);
 			break;
 		}
@@ -318,12 +333,14 @@
 	if (interrupt & ONENAND_INT_READ) {
 		int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
 		if (ecc) {
-			printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
 			if (ecc & ONENAND_ECC_2BIT_ALL) {
+				printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
 				mtd->ecc_stats.failed++;
 				return ecc;
-			} else if (ecc & ONENAND_ECC_1BIT_ALL)
+			} else if (ecc & ONENAND_ECC_1BIT_ALL) {
+				printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc);
 				mtd->ecc_stats.corrected++;
+			}
 		}
 	} else if (state == FL_READING) {
 		printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
@@ -445,8 +462,9 @@
 	struct onenand_chip *this = mtd->priv;
 
 	if (ONENAND_CURRENT_BUFFERRAM(this)) {
+		/* Note: the 'this->writesize' is a real page size */
 		if (area == ONENAND_DATARAM)
-			return mtd->writesize;
+			return this->writesize;
 		if (area == ONENAND_SPARERAM)
 			return mtd->oobsize;
 	}
@@ -572,6 +590,30 @@
 }
 
 /**
+ * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
+ * @param mtd		MTD data structure
+ * @param addr		address to check
+ * @return		blockpage address
+ *
+ * Get blockpage address at 2x program mode
+ */
+static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
+{
+	struct onenand_chip *this = mtd->priv;
+	int blockpage, block, page;
+
+	/* Calculate the even block number */
+	block = (int) (addr >> this->erase_shift) & ~1;
+	/* Is it the odd plane? */
+	if (addr & this->writesize)
+		block++;
+	page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
+	blockpage = (block << 7) | page;
+
+	return blockpage;
+}
+
+/**
  * onenand_check_bufferram - [GENERIC] Check BufferRAM information
  * @param mtd		MTD data structure
  * @param addr		address to check
@@ -585,7 +627,10 @@
 	int blockpage, found = 0;
 	unsigned int i;
 
-	blockpage = (int) (addr >> this->page_shift);
+	if (ONENAND_IS_2PLANE(this))
+		blockpage = onenand_get_2x_blockpage(mtd, addr);
+	else
+		blockpage = (int) (addr >> this->page_shift);
 
 	/* Is there valid data? */
 	i = ONENAND_CURRENT_BUFFERRAM(this);
@@ -625,7 +670,10 @@
 	int blockpage;
 	unsigned int i;
 
-	blockpage = (int) (addr >> this->page_shift);
+	if (ONENAND_IS_2PLANE(this))
+		blockpage = onenand_get_2x_blockpage(mtd, addr);
+	else
+		blockpage = (int) (addr >> this->page_shift);
 
 	/* Invalidate another BufferRAM */
 	i = ONENAND_NEXT_BUFFERRAM(this);
@@ -717,111 +765,6 @@
 }
 
 /**
- * onenand_read - [MTD Interface] Read data from flash
- * @param mtd		MTD device structure
- * @param from		offset to read from
- * @param len		number of bytes to read
- * @param retlen	pointer to variable to store the number of read bytes
- * @param buf		the databuffer to put data
- *
- * Read with ecc
-*/
-static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
-	size_t *retlen, u_char *buf)
-{
-	struct onenand_chip *this = mtd->priv;
-	struct mtd_ecc_stats stats;
-	int read = 0, column;
-	int thislen;
-	int ret = 0, boundary = 0;
-
-	DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
-
-	/* Do not allow reads past end of device */
-	if ((from + len) > mtd->size) {
-		printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n");
-		*retlen = 0;
-		return -EINVAL;
-	}
-
-	/* Grab the lock and see if the device is available */
-	onenand_get_device(mtd, FL_READING);
-
-	stats = mtd->ecc_stats;
-
- 	/* Read-while-load method */
-
- 	/* Do first load to bufferRAM */
- 	if (read < len) {
- 		if (!onenand_check_bufferram(mtd, from)) {
- 			this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize);
- 			ret = this->wait(mtd, FL_READING);
- 			onenand_update_bufferram(mtd, from, !ret);
- 		}
- 	}
-
- 	thislen = min_t(int, mtd->writesize, len - read);
- 	column = from & (mtd->writesize - 1);
- 	if (column + thislen > mtd->writesize)
- 		thislen = mtd->writesize - column;
-
- 	while (!ret) {
- 		/* If there is more to load then start next load */
- 		from += thislen;
- 		if (read + thislen < len) {
- 			this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize);
- 			/*
- 			 * Chip boundary handling in DDP
- 			 * Now we issued chip 1 read and pointed chip 1
- 			 * bufferam so we have to point chip 0 bufferam.
- 			 */
- 			if (ONENAND_IS_DDP(this) &&
- 			    unlikely(from == (this->chipsize >> 1))) {
- 				this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
- 				boundary = 1;
- 			} else
- 				boundary = 0;
- 			ONENAND_SET_PREV_BUFFERRAM(this);
- 		}
- 		/* While load is going, read from last bufferRAM */
- 		this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
- 		/* See if we are done */
- 		read += thislen;
- 		if (read == len)
- 			break;
- 		/* Set up for next read from bufferRAM */
- 		if (unlikely(boundary))
- 			this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
- 		ONENAND_SET_NEXT_BUFFERRAM(this);
- 		buf += thislen;
- 		thislen = min_t(int, mtd->writesize, len - read);
- 		column = 0;
- 		cond_resched();
- 		/* Now wait for load */
- 		ret = this->wait(mtd, FL_READING);
- 		onenand_update_bufferram(mtd, from, !ret);
- 	}
-
-	/* Deselect and wake up anyone waiting on the device */
-	onenand_release_device(mtd);
-
-	/*
-	 * Return success, if no ECC failures, else -EBADMSG
-	 * fs driver will take care of that, because
-	 * retlen == desired len and result == -EBADMSG
-	 */
-	*retlen = read;
-
-	if (mtd->ecc_stats.failed - stats.failed)
-		return -EBADMSG;
-
-	if (ret)
-		return ret;
-
-	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
-}
-
-/**
  * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
  * @param mtd		MTD device structure
  * @param buf		destination address
@@ -864,27 +807,155 @@
 }
 
 /**
- * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
+ * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
  * @param mtd		MTD device structure
  * @param from		offset to read from
- * @param len		number of bytes to read
- * @param retlen	pointer to variable to store the number of read bytes
- * @param buf		the databuffer to put data
- * @param mode		operation mode
+ * @param ops:		oob operation description structure
+ *
+ * OneNAND read main and/or out-of-band data
+ */
+static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
+				struct mtd_oob_ops *ops)
+{
+	struct onenand_chip *this = mtd->priv;
+	struct mtd_ecc_stats stats;
+	size_t len = ops->len;
+	size_t ooblen = ops->ooblen;
+	u_char *buf = ops->datbuf;
+	u_char *oobbuf = ops->oobbuf;
+	int read = 0, column, thislen;
+	int oobread = 0, oobcolumn, thisooblen, oobsize;
+	int ret = 0, boundary = 0;
+	int writesize = this->writesize;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+	if (ops->mode == MTD_OOB_AUTO)
+		oobsize = this->ecclayout->oobavail;
+	else
+		oobsize = mtd->oobsize;
+
+	oobcolumn = from & (mtd->oobsize - 1);
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n");
+		ops->retlen = 0;
+		ops->oobretlen = 0;
+		return -EINVAL;
+	}
+
+	stats = mtd->ecc_stats;
+
+ 	/* Read-while-load method */
+
+ 	/* Do first load to bufferRAM */
+ 	if (read < len) {
+ 		if (!onenand_check_bufferram(mtd, from)) {
+			this->command(mtd, ONENAND_CMD_READ, from, writesize);
+ 			ret = this->wait(mtd, FL_READING);
+ 			onenand_update_bufferram(mtd, from, !ret);
+ 		}
+ 	}
+
+	thislen = min_t(int, writesize, len - read);
+	column = from & (writesize - 1);
+	if (column + thislen > writesize)
+		thislen = writesize - column;
+
+ 	while (!ret) {
+ 		/* If there is more to load then start next load */
+ 		from += thislen;
+ 		if (read + thislen < len) {
+			this->command(mtd, ONENAND_CMD_READ, from, writesize);
+ 			/*
+ 			 * Chip boundary handling in DDP
+ 			 * Now we issued chip 1 read and pointed chip 1
+ 			 * bufferam so we have to point chip 0 bufferam.
+ 			 */
+ 			if (ONENAND_IS_DDP(this) &&
+ 			    unlikely(from == (this->chipsize >> 1))) {
+ 				this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
+ 				boundary = 1;
+ 			} else
+ 				boundary = 0;
+ 			ONENAND_SET_PREV_BUFFERRAM(this);
+ 		}
+ 		/* While load is going, read from last bufferRAM */
+ 		this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
+
+		/* Read oob area if needed */
+		if (oobbuf) {
+			thisooblen = oobsize - oobcolumn;
+			thisooblen = min_t(int, thisooblen, ooblen - oobread);
+
+			if (ops->mode == MTD_OOB_AUTO)
+				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
+			else
+				this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
+			oobread += thisooblen;
+			oobbuf += thisooblen;
+			oobcolumn = 0;
+		}
+
+ 		/* See if we are done */
+ 		read += thislen;
+ 		if (read == len)
+ 			break;
+ 		/* Set up for next read from bufferRAM */
+ 		if (unlikely(boundary))
+ 			this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
+ 		ONENAND_SET_NEXT_BUFFERRAM(this);
+ 		buf += thislen;
+		thislen = min_t(int, writesize, len - read);
+ 		column = 0;
+ 		cond_resched();
+ 		/* Now wait for load */
+ 		ret = this->wait(mtd, FL_READING);
+ 		onenand_update_bufferram(mtd, from, !ret);
+ 	}
+
+	/*
+	 * Return success, if no ECC failures, else -EBADMSG
+	 * fs driver will take care of that, because
+	 * retlen == desired len and result == -EBADMSG
+	 */
+	ops->retlen = read;
+	ops->oobretlen = oobread;
+
+	if (mtd->ecc_stats.failed - stats.failed)
+		return -EBADMSG;
+
+	if (ret)
+		return ret;
+
+	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
+/**
+ * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
+ * @param mtd		MTD device structure
+ * @param from		offset to read from
+ * @param ops:		oob operation description structure
  *
  * OneNAND read out-of-band data from the spare area
  */
-static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-			size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
+static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
+			struct mtd_oob_ops *ops)
 {
 	struct onenand_chip *this = mtd->priv;
 	int read = 0, thislen, column, oobsize;
+	size_t len = ops->ooblen;
+	mtd_oob_mode_t mode = ops->mode;
+	u_char *buf = ops->oobbuf;
 	int ret = 0;
 
-	DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+	from += ops->ooboffs;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
 
 	/* Initialize return length value */
-	*retlen = 0;
+	ops->oobretlen = 0;
 
 	if (mode == MTD_OOB_AUTO)
 		oobsize = this->ecclayout->oobavail;
@@ -894,7 +965,7 @@
 	column = from & (mtd->oobsize - 1);
 
 	if (unlikely(column >= oobsize)) {
-		printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n");
+		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n");
 		return -EINVAL;
 	}
 
@@ -902,13 +973,10 @@
 	if (unlikely(from >= mtd->size ||
 		     column + len > ((mtd->size >> this->page_shift) -
 				     (from >> this->page_shift)) * oobsize)) {
-		printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n");
+		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n");
 		return -EINVAL;
 	}
 
-	/* Grab the lock and see if the device is available */
-	onenand_get_device(mtd, FL_READING);
-
 	while (read < len) {
 		cond_resched();
 
@@ -928,7 +996,7 @@
 			this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
 
 		if (ret) {
-			printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret);
+			printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
 			break;
 		}
 
@@ -947,22 +1015,52 @@
 		}
 	}
 
-	/* Deselect and wake up anyone waiting on the device */
-	onenand_release_device(mtd);
-
-	*retlen = read;
+	ops->oobretlen = read;
 	return ret;
 }
 
 /**
- * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band
+ * onenand_read - [MTD Interface] Read data from flash
+ * @param mtd		MTD device structure
+ * @param from		offset to read from
+ * @param len		number of bytes to read
+ * @param retlen	pointer to variable to store the number of read bytes
+ * @param buf		the databuffer to put data
+ *
+ * Read with ecc
+*/
+static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
+	size_t *retlen, u_char *buf)
+{
+	struct mtd_oob_ops ops = {
+		.len	= len,
+		.ooblen	= 0,
+		.datbuf	= buf,
+		.oobbuf	= NULL,
+	};
+	int ret;
+
+	onenand_get_device(mtd, FL_READING);
+	ret = onenand_read_ops_nolock(mtd, from, &ops);
+	onenand_release_device(mtd);
+
+	*retlen = ops.retlen;
+	return ret;
+}
+
+/**
+ * onenand_read_oob - [MTD Interface] Read main and/or out-of-band
  * @param mtd:		MTD device structure
  * @param from:		offset to read from
  * @param ops:		oob operation description structure
+
+ * Read main and/or out-of-band
  */
 static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
 			    struct mtd_oob_ops *ops)
 {
+	int ret;
+
 	switch (ops->mode) {
 	case MTD_OOB_PLACE:
 	case MTD_OOB_AUTO:
@@ -972,8 +1070,15 @@
 	default:
 		return -EINVAL;
 	}
-	return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
-				   &ops->oobretlen, ops->oobbuf, ops->mode);
+
+	onenand_get_device(mtd, FL_READING);
+	if (ops->datbuf)
+		ret = onenand_read_ops_nolock(mtd, from, ops);
+	else
+		ret = onenand_read_oob_nolock(mtd, from, ops);
+	onenand_release_device(mtd);
+
+	return ret;
 }
 
 /**
@@ -1079,7 +1184,7 @@
 		/* Read more? */
 		if (read < len) {
 			/* Update Page size */
-			from += mtd->writesize;
+			from += this->writesize;
 			column = 0;
 		}
 	}
@@ -1097,7 +1202,6 @@
  * @param mtd		MTD device structure
  * @param buf		the databuffer to verify
  * @param to		offset to read from
- *
  */
 static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
 {
@@ -1125,7 +1229,6 @@
  * @param buf          the databuffer to verify
  * @param addr         offset to read from
  * @param len          number of bytes to read and compare
- *
  */
 static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
 {
@@ -1135,12 +1238,12 @@
 	int thislen, column;
 
 	while (len != 0) {
-		thislen = min_t(int, mtd->writesize, len);
-		column = addr & (mtd->writesize - 1);
-		if (column + thislen > mtd->writesize)
-			thislen = mtd->writesize - column;
+		thislen = min_t(int, this->writesize, len);
+		column = addr & (this->writesize - 1);
+		if (column + thislen > this->writesize)
+			thislen = this->writesize - column;
 
-		this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
+		this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
 
 		onenand_update_bufferram(mtd, addr, 0);
 
@@ -1171,103 +1274,6 @@
 #define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)
 
 /**
- * onenand_write - [MTD Interface] write buffer to FLASH
- * @param mtd		MTD device structure
- * @param to		offset to write to
- * @param len		number of bytes to write
- * @param retlen	pointer to variable to store the number of written bytes
- * @param buf		the data to write
- *
- * Write with ECC
- */
-static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
-	size_t *retlen, const u_char *buf)
-{
-	struct onenand_chip *this = mtd->priv;
-	int written = 0;
-	int ret = 0;
-	int column, subpage;
-
-	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
-
-	/* Initialize retlen, in case of early exit */
-	*retlen = 0;
-
-	/* Do not allow writes past end of device */
-	if (unlikely((to + len) > mtd->size)) {
-		printk(KERN_ERR "onenand_write: Attempt write to past end of device\n");
-		return -EINVAL;
-	}
-
-	/* Reject writes, which are not page aligned */
-        if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
-                printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n");
-                return -EINVAL;
-        }
-
-	column = to & (mtd->writesize - 1);
-
-	/* Grab the lock and see if the device is available */
-	onenand_get_device(mtd, FL_WRITING);
-
-	/* Loop until all data write */
-	while (written < len) {
-		int thislen = min_t(int, mtd->writesize - column, len - written);
-		u_char *wbuf = (u_char *) buf;
-
-		cond_resched();
-
-		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
-
-		/* Partial page write */
-		subpage = thislen < mtd->writesize;
-		if (subpage) {
-			memset(this->page_buf, 0xff, mtd->writesize);
-			memcpy(this->page_buf + column, buf, thislen);
-			wbuf = this->page_buf;
-		}
-
-		this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
-		this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
-
-		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
-
-		ret = this->wait(mtd, FL_WRITING);
-
-		/* In partial page write we don't update bufferram */
-		onenand_update_bufferram(mtd, to, !ret && !subpage);
-
-		if (ret) {
-			printk(KERN_ERR "onenand_write: write filaed %d\n", ret);
-			break;
-		}
-
-		/* Only check verify write turn on */
-		ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
-		if (ret) {
-			printk(KERN_ERR "onenand_write: verify failed %d\n", ret);
-			break;
-		}
-
-		written += thislen;
-
-		if (written == len)
-			break;
-
-		column = 0;
-		to += thislen;
-		buf += thislen;
-	}
-
-	/* Deselect and wake up anyone waiting on the device */
-	onenand_release_device(mtd);
-
-	*retlen = written;
-
-	return ret;
-}
-
-/**
  * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
  * @param mtd		MTD device structure
  * @param oob_buf	oob buffer
@@ -1309,7 +1315,137 @@
 }
 
 /**
- * onenand_do_write_oob - [Internal] OneNAND write out-of-band
+ * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
+ * @param mtd		MTD device structure
+ * @param to		offset to write to
+ * @param ops		oob operation description structure
+ *
+ * Write main and/or oob with ECC
+ */
+static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
+				struct mtd_oob_ops *ops)
+{
+	struct onenand_chip *this = mtd->priv;
+	int written = 0, column, thislen, subpage;
+	int oobwritten = 0, oobcolumn, thisooblen, oobsize;
+	size_t len = ops->len;
+	size_t ooblen = ops->ooblen;
+	const u_char *buf = ops->datbuf;
+	const u_char *oob = ops->oobbuf;
+	u_char *oobbuf;
+	int ret = 0;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+	/* Initialize retlen, in case of early exit */
+	ops->retlen = 0;
+	ops->oobretlen = 0;
+
+	/* Do not allow writes past end of device */
+	if (unlikely((to + len) > mtd->size)) {
+		printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
+		return -EINVAL;
+	}
+
+	/* Reject writes, which are not page aligned */
+        if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
+                printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
+                return -EINVAL;
+        }
+
+	if (ops->mode == MTD_OOB_AUTO)
+		oobsize = this->ecclayout->oobavail;
+	else
+		oobsize = mtd->oobsize;
+
+	oobcolumn = to & (mtd->oobsize - 1);
+
+	column = to & (mtd->writesize - 1);
+
+	/* Loop until all data write */
+	while (written < len) {
+		u_char *wbuf = (u_char *) buf;
+
+		thislen = min_t(int, mtd->writesize - column, len - written);
+		thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
+
+		cond_resched();
+
+		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
+
+		/* Partial page write */
+		subpage = thislen < mtd->writesize;
+		if (subpage) {
+			memset(this->page_buf, 0xff, mtd->writesize);
+			memcpy(this->page_buf + column, buf, thislen);
+			wbuf = this->page_buf;
+		}
+
+		this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
+
+		if (oob) {
+			oobbuf = this->oob_buf;
+
+			/* We send data to spare ram with oobsize
+			 * to prevent byte access */
+			memset(oobbuf, 0xff, mtd->oobsize);
+			if (ops->mode == MTD_OOB_AUTO)
+				onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
+			else
+				memcpy(oobbuf + oobcolumn, oob, thisooblen);
+
+			oobwritten += thisooblen;
+			oob += thisooblen;
+			oobcolumn = 0;
+		} else
+			oobbuf = (u_char *) ffchars;
+
+		this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
+
+		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
+
+		ret = this->wait(mtd, FL_WRITING);
+
+		/* In partial page write we don't update bufferram */
+		onenand_update_bufferram(mtd, to, !ret && !subpage);
+		if (ONENAND_IS_2PLANE(this)) {
+			ONENAND_SET_BUFFERRAM1(this);
+			onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
+		}
+
+		if (ret) {
+			printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+			break;
+		}
+
+		/* Only check verify write turn on */
+		ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
+		if (ret) {
+			printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
+			break;
+		}
+
+		written += thislen;
+
+		if (written == len)
+			break;
+
+		column = 0;
+		to += thislen;
+		buf += thislen;
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	onenand_release_device(mtd);
+
+	ops->retlen = written;
+
+	return ret;
+}
+
+
+/**
+ * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
  * @param mtd		MTD device structure
  * @param to		offset to write to
  * @param len		number of bytes to write
@@ -1319,18 +1455,23 @@
  *
  * OneNAND write out-of-band
  */
-static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
-				size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
+static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
+				    struct mtd_oob_ops *ops)
 {
 	struct onenand_chip *this = mtd->priv;
 	int column, ret = 0, oobsize;
 	int written = 0;
 	u_char *oobbuf;
+	size_t len = ops->ooblen;
+	const u_char *buf = ops->oobbuf;
+	mtd_oob_mode_t mode = ops->mode;
 
-	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+	to += ops->ooboffs;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
 
 	/* Initialize retlen, in case of early exit */
-	*retlen = 0;
+	ops->oobretlen = 0;
 
 	if (mode == MTD_OOB_AUTO)
 		oobsize = this->ecclayout->oobavail;
@@ -1340,13 +1481,13 @@
 	column = to & (mtd->oobsize - 1);
 
 	if (unlikely(column >= oobsize)) {
-		printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n");
+		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n");
 		return -EINVAL;
 	}
 
 	/* For compatibility with NAND: Do not allow write past end of page */
 	if (unlikely(column + len > oobsize)) {
-		printk(KERN_ERR "onenand_write_oob: "
+		printk(KERN_ERR "onenand_write_oob_nolock: "
 		      "Attempt to write past end of page\n");
 		return -EINVAL;
 	}
@@ -1355,13 +1496,10 @@
 	if (unlikely(to >= mtd->size ||
 		     column + len > ((mtd->size >> this->page_shift) -
 				     (to >> this->page_shift)) * oobsize)) {
-		printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n");
+		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n");
 		return -EINVAL;
 	}
 
-	/* Grab the lock and see if the device is available */
-	onenand_get_device(mtd, FL_WRITING);
-
 	oobbuf = this->oob_buf;
 
 	/* Loop until all data write */
@@ -1384,16 +1522,20 @@
 		this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
 
 		onenand_update_bufferram(mtd, to, 0);
+		if (ONENAND_IS_2PLANE(this)) {
+			ONENAND_SET_BUFFERRAM1(this);
+			onenand_update_bufferram(mtd, to + this->writesize, 0);
+		}
 
 		ret = this->wait(mtd, FL_WRITING);
 		if (ret) {
-			printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret);
+			printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret);
 			break;
 		}
 
 		ret = onenand_verify_oob(mtd, oobbuf, to);
 		if (ret) {
-			printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret);
+			printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret);
 			break;
 		}
 
@@ -1406,11 +1548,37 @@
 		column = 0;
 	}
 
-	/* Deselect and wake up anyone waiting on the device */
+	ops->oobretlen = written;
+
+	return ret;
+}
+
+/**
+ * onenand_write - [MTD Interface] write buffer to FLASH
+ * @param mtd		MTD device structure
+ * @param to		offset to write to
+ * @param len		number of bytes to write
+ * @param retlen	pointer to variable to store the number of written bytes
+ * @param buf		the data to write
+ *
+ * Write with ECC
+ */
+static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
+	size_t *retlen, const u_char *buf)
+{
+	struct mtd_oob_ops ops = {
+		.len	= len,
+		.ooblen	= 0,
+		.datbuf	= (u_char *) buf,
+		.oobbuf	= NULL,
+	};
+	int ret;
+
+	onenand_get_device(mtd, FL_WRITING);
+	ret = onenand_write_ops_nolock(mtd, to, &ops);
 	onenand_release_device(mtd);
 
-	*retlen = written;
-
+	*retlen = ops.retlen;
 	return ret;
 }
 
@@ -1423,6 +1591,8 @@
 static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
 			     struct mtd_oob_ops *ops)
 {
+	int ret;
+
 	switch (ops->mode) {
 	case MTD_OOB_PLACE:
 	case MTD_OOB_AUTO:
@@ -1432,21 +1602,27 @@
 	default:
 		return -EINVAL;
 	}
-	return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
-				    &ops->oobretlen, ops->oobbuf, ops->mode);
+
+	onenand_get_device(mtd, FL_WRITING);
+	if (ops->datbuf)
+		ret = onenand_write_ops_nolock(mtd, to, ops);
+	else
+		ret = onenand_write_oob_nolock(mtd, to, ops);
+	onenand_release_device(mtd);
+
+	return ret;
 }
 
 /**
- * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
  * @param mtd		MTD device structure
  * @param ofs		offset from device start
- * @param getchip	0, if the chip is already selected
  * @param allowbbt	1, if its allowed to access the bbt area
  *
  * Check, if the block is bad. Either by reading the bad block table or
  * calling of the scan function.
  */
-static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
 {
 	struct onenand_chip *this = mtd->priv;
 	struct bbm_info *bbm = this->bbm;
@@ -1507,7 +1683,7 @@
 		cond_resched();
 
 		/* Check if we have a bad block, we do not erase bad blocks */
-		if (onenand_block_checkbad(mtd, addr, 0, 0)) {
+		if (onenand_block_isbad_nolock(mtd, addr, 0)) {
 			printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr);
 			instr->state = MTD_ERASE_FAILED;
 			goto erase_exit;
@@ -1571,11 +1747,16 @@
  */
 static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
 {
+	int ret;
+
 	/* Check for invalid offset */
 	if (ofs > mtd->size)
 		return -EINVAL;
 
-	return onenand_block_checkbad(mtd, ofs, 1, 0);
+	onenand_get_device(mtd, FL_READING);
+	ret = onenand_block_isbad_nolock(mtd, ofs, 0);
+	onenand_release_device(mtd);
+	return ret;
 }
 
 /**
@@ -1591,7 +1772,12 @@
 	struct onenand_chip *this = mtd->priv;
 	struct bbm_info *bbm = this->bbm;
 	u_char buf[2] = {0, 0};
-	size_t retlen;
+	struct mtd_oob_ops ops = {
+		.mode = MTD_OOB_PLACE,
+		.ooblen = 2,
+		.oobbuf = buf,
+		.ooboffs = 0,
+	};
 	int block;
 
 	/* Get block number */
@@ -1601,7 +1787,7 @@
 
         /* We write two bytes, so we dont have to mess with 16 bit access */
         ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
-        return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
+        return onenand_write_oob_nolock(mtd, ofs, &ops);
 }
 
 /**
@@ -1624,7 +1810,10 @@
 		return ret;
 	}
 
-	return this->block_markbad(mtd, ofs);
+	onenand_get_device(mtd, FL_WRITING);
+	ret = this->block_markbad(mtd, ofs);
+	onenand_release_device(mtd);
+	return ret;
 }
 
 /**
@@ -1823,13 +2012,19 @@
 		size_t *retlen, u_char *buf)
 {
 	struct onenand_chip *this = mtd->priv;
+	struct mtd_oob_ops ops = {
+		.len	= len,
+		.ooblen	= 0,
+		.datbuf	= buf,
+		.oobbuf	= NULL,
+	};
 	int ret;
 
 	/* Enter OTP access mode */
 	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
 	this->wait(mtd, FL_OTPING);
 
-	ret = mtd->read(mtd, from, len, retlen, buf);
+	ret = onenand_read_ops_nolock(mtd, from, &ops);
 
 	/* Exit OTP access mode */
 	this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -1841,19 +2036,20 @@
 /**
  * do_otp_write - [DEFAULT] Write OTP block area
  * @param mtd		MTD device structure
- * @param from		The offset to write
+ * @param to		The offset to write
  * @param len		number of bytes to write
  * @param retlen	pointer to variable to store the number of write bytes
  * @param buf		the databuffer to put/get data
  *
  * Write OTP block area.
  */
-static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len,
+static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len,
 		size_t *retlen, u_char *buf)
 {
 	struct onenand_chip *this = mtd->priv;
 	unsigned char *pbuf = buf;
 	int ret;
+	struct mtd_oob_ops ops;
 
 	/* Force buffer page aligned */
 	if (len < mtd->writesize) {
@@ -1867,7 +2063,12 @@
 	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
 	this->wait(mtd, FL_OTPING);
 
-	ret = mtd->write(mtd, from, len, retlen, pbuf);
+	ops.len = len;
+	ops.ooblen = 0;
+	ops.datbuf = pbuf;
+	ops.oobbuf = NULL;
+	ret = onenand_write_ops_nolock(mtd, to, &ops);
+	*retlen = ops.retlen;
 
 	/* Exit OTP access mode */
 	this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -1890,13 +2091,21 @@
 		size_t *retlen, u_char *buf)
 {
 	struct onenand_chip *this = mtd->priv;
+	struct mtd_oob_ops ops = {
+		.mode = MTD_OOB_PLACE,
+		.ooblen = len,
+		.oobbuf = buf,
+		.ooboffs = 0,
+	};
 	int ret;
 
 	/* Enter OTP access mode */
 	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
 	this->wait(mtd, FL_OTPING);
 
-	ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
+	ret = onenand_write_oob_nolock(mtd, from, &ops);
+
+	*retlen = ops.oobretlen;
 
 	/* Exit OTP access mode */
 	this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -1943,13 +2152,16 @@
 	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
 		return 0;
 
+	onenand_get_device(mtd, FL_OTPING);
 	while (len > 0 && otp_pages > 0) {
 		if (!action) {	/* OTP Info functions */
 			struct otp_info *otpinfo;
 
 			len -= sizeof(struct otp_info);
-			if (len <= 0)
-				return -ENOSPC;
+			if (len <= 0) {
+				ret = -ENOSPC;
+				break;
+			}
 
 			otpinfo = (struct otp_info *) buf;
 			otpinfo->start = from;
@@ -1969,13 +2181,14 @@
 			len -= size;
 			*retlen += size;
 
-			if (ret < 0)
-				return ret;
+			if (ret)
+				break;
 		}
 		otp_pages--;
 	}
+	onenand_release_device(mtd);
 
-	return 0;
+	return ret;
 }
 
 /**
@@ -2107,6 +2320,7 @@
  *
  * Check and set OneNAND features
  * - lock scheme
+ * - two plane
  */
 static void onenand_check_features(struct mtd_info *mtd)
 {
@@ -2118,19 +2332,35 @@
 	process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
 
 	/* Lock scheme */
-	if (density >= ONENAND_DEVICE_DENSITY_1Gb) {
+	switch (density) {
+	case ONENAND_DEVICE_DENSITY_4Gb:
+		this->options |= ONENAND_HAS_2PLANE;
+
+	case ONENAND_DEVICE_DENSITY_2Gb:
+		/* 2Gb DDP don't have 2 plane */
+		if (!ONENAND_IS_DDP(this))
+			this->options |= ONENAND_HAS_2PLANE;
+		this->options |= ONENAND_HAS_UNLOCK_ALL;
+
+	case ONENAND_DEVICE_DENSITY_1Gb:
 		/* A-Die has all block unlock */
-		if (process) {
-			printk(KERN_DEBUG "Chip support all block unlock\n");
+		if (process)
 			this->options |= ONENAND_HAS_UNLOCK_ALL;
-		}
-	} else {
-		/* Some OneNAND has continues lock scheme */
-		if (!process) {
-			printk(KERN_DEBUG "Lock scheme is Continues Lock\n");
+		break;
+
+	default:
+		/* Some OneNAND has continuous lock scheme */
+		if (!process)
 			this->options |= ONENAND_HAS_CONT_LOCK;
-		}
+		break;
 	}
+
+	if (this->options & ONENAND_HAS_CONT_LOCK)
+		printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
+	if (this->options & ONENAND_HAS_UNLOCK_ALL)
+		printk(KERN_DEBUG "Chip support all block unlock\n");
+	if (this->options & ONENAND_HAS_2PLANE)
+		printk(KERN_DEBUG "Chip has 2 plane\n");
 }
 
 /**
@@ -2154,7 +2384,7 @@
                 (16 << density),
                 vcc ? "2.65/3.3" : "1.8",
                 device);
-	printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version);
+	printk(KERN_INFO "OneNAND version = 0x%04x\n", version);
 }
 
 static const struct onenand_manufacturers onenand_manuf_ids[] = {
@@ -2257,6 +2487,8 @@
 	this->erase_shift = ffs(mtd->erasesize) - 1;
 	this->page_shift = ffs(mtd->writesize) - 1;
 	this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
+	/* It's real page size */
+	this->writesize = mtd->writesize;
 
 	/* REVIST: Multichip handling */
 
@@ -2265,6 +2497,17 @@
 	/* Check OneNAND features */
 	onenand_check_features(mtd);
 
+	/*
+	 * We emulate the 4KiB page and 256KiB erase block size
+	 * But oobsize is still 64 bytes.
+	 * It is only valid if you turn on 2X program support,
+	 * Otherwise it will be ignored by compiler.
+	 */
+	if (ONENAND_IS_2PLANE(this)) {
+		mtd->writesize <<= 1;
+		mtd->erasesize <<= 1;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c
new file mode 100644
index 0000000..0d89ad5
--- /dev/null
+++ b/drivers/mtd/onenand/onenand_sim.c
@@ -0,0 +1,495 @@
+/*
+ *  linux/drivers/mtd/onenand/onenand_sim.c
+ *
+ *  The OneNAND simulator
+ *
+ *  Copyright © 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/onenand.h>
+
+#include <linux/io.h>
+
+#ifndef CONFIG_ONENAND_SIM_MANUFACTURER
+#define CONFIG_ONENAND_SIM_MANUFACTURER         0xec
+#endif
+#ifndef CONFIG_ONENAND_SIM_DEVICE_ID
+#define CONFIG_ONENAND_SIM_DEVICE_ID            0x04
+#endif
+#ifndef CONFIG_ONENAND_SIM_VERSION_ID
+#define CONFIG_ONENAND_SIM_VERSION_ID           0x1e
+#endif
+
+static int manuf_id	= CONFIG_ONENAND_SIM_MANUFACTURER;
+static int device_id	= CONFIG_ONENAND_SIM_DEVICE_ID;
+static int version_id	= CONFIG_ONENAND_SIM_VERSION_ID;
+
+struct onenand_flash {
+	void __iomem *base;
+	void __iomem *data;
+};
+
+#define ONENAND_CORE(flash)		(flash->data)
+#define ONENAND_CORE_SPARE(flash, this, offset)				\
+	((flash->data) + (this->chipsize) + (offset >> 5))
+
+#define ONENAND_MAIN_AREA(this, offset)					\
+	(this->base + ONENAND_DATARAM + offset)
+
+#define ONENAND_SPARE_AREA(this, offset)				\
+	(this->base + ONENAND_SPARERAM + offset)
+
+#define ONENAND_GET_WP_STATUS(this)					\
+	(readw(this->base + ONENAND_REG_WP_STATUS))
+
+#define ONENAND_SET_WP_STATUS(v, this)					\
+	(writew(v, this->base + ONENAND_REG_WP_STATUS))
+
+/* It has all 0xff chars */
+#define MAX_ONENAND_PAGESIZE		(2048 + 64)
+static unsigned char *ffchars;
+
+static struct mtd_partition os_partitions[] = {
+	{
+		.name		= "OneNAND simulator partition",
+		.offset		= 0,
+		.size		= MTDPART_SIZ_FULL,
+	},
+};
+
+/*
+ * OneNAND simulator mtd
+ */
+struct onenand_info {
+	struct mtd_info		mtd;
+	struct mtd_partition	*parts;
+	struct onenand_chip	onenand;
+	struct onenand_flash	flash;
+};
+
+static struct onenand_info *info;
+
+#define DPRINTK(format, args...)					\
+do {									\
+	printk(KERN_DEBUG "%s[%d]: " format "\n", __func__,		\
+			   __LINE__, ##args);				\
+} while (0)
+
+/**
+ * onenand_lock_handle - Handle Lock scheme
+ * @param this		OneNAND device structure
+ * @param cmd		The command to be sent
+ *
+ * Send lock command to OneNAND device.
+ * The lock scheme is depends on chip type.
+ */
+static void onenand_lock_handle(struct onenand_chip *this, int cmd)
+{
+	int block_lock_scheme;
+	int status;
+
+	status = ONENAND_GET_WP_STATUS(this);
+	block_lock_scheme = !(this->options & ONENAND_HAS_CONT_LOCK);
+
+	switch (cmd) {
+	case ONENAND_CMD_UNLOCK:
+		if (block_lock_scheme)
+			ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
+		else
+			ONENAND_SET_WP_STATUS(status | ONENAND_WP_US, this);
+		break;
+
+	case ONENAND_CMD_LOCK:
+		if (block_lock_scheme)
+			ONENAND_SET_WP_STATUS(ONENAND_WP_LS, this);
+		else
+			ONENAND_SET_WP_STATUS(status | ONENAND_WP_LS, this);
+		break;
+
+	case ONENAND_CMD_LOCK_TIGHT:
+		if (block_lock_scheme)
+			ONENAND_SET_WP_STATUS(ONENAND_WP_LTS, this);
+		else
+			ONENAND_SET_WP_STATUS(status | ONENAND_WP_LTS, this);
+		break;
+
+	default:
+		break;
+	}
+}
+
+/**
+ * onenand_bootram_handle - Handle BootRAM area
+ * @param this		OneNAND device structure
+ * @param cmd		The command to be sent
+ *
+ * Emulate BootRAM area. It is possible to do basic operation using BootRAM.
+ */
+static void onenand_bootram_handle(struct onenand_chip *this, int cmd)
+{
+	switch (cmd) {
+	case ONENAND_CMD_READID:
+		writew(manuf_id, this->base);
+		writew(device_id, this->base + 2);
+		writew(version_id, this->base + 4);
+		break;
+
+	default:
+		/* REVIST: Handle other commands */
+		break;
+	}
+}
+
+/**
+ * onenand_update_interrupt - Set interrupt register
+ * @param this         OneNAND device structure
+ * @param cmd          The command to be sent
+ *
+ * Update interrupt register. The status is depends on command.
+ */
+static void onenand_update_interrupt(struct onenand_chip *this, int cmd)
+{
+	int interrupt = ONENAND_INT_MASTER;
+
+	switch (cmd) {
+	case ONENAND_CMD_READ:
+	case ONENAND_CMD_READOOB:
+		interrupt |= ONENAND_INT_READ;
+		break;
+
+	case ONENAND_CMD_PROG:
+	case ONENAND_CMD_PROGOOB:
+		interrupt |= ONENAND_INT_WRITE;
+		break;
+
+	case ONENAND_CMD_ERASE:
+		interrupt |= ONENAND_INT_ERASE;
+		break;
+
+	case ONENAND_CMD_RESET:
+		interrupt |= ONENAND_INT_RESET;
+		break;
+
+	default:
+		break;
+	}
+
+	writew(interrupt, this->base + ONENAND_REG_INTERRUPT);
+}
+
+/**
+ * onenand_check_overwrite - Check over-write if happend
+ * @param dest		The destination pointer
+ * @param src		The source pointer
+ * @param count		The length to be check
+ * @return		0 on same, otherwise 1
+ *
+ * Compare the source with destination
+ */
+static int onenand_check_overwrite(void *dest, void *src, size_t count)
+{
+	unsigned int *s = (unsigned int *) src;
+	unsigned int *d = (unsigned int *) dest;
+	int i;
+
+	count >>= 2;
+	for (i = 0; i < count; i++)
+		if ((*s++ ^ *d++) != 0)
+			return 1;
+
+	return 0;
+}
+
+/**
+ * onenand_data_handle - Handle OneNAND Core and DataRAM
+ * @param this		OneNAND device structure
+ * @param cmd		The command to be sent
+ * @param dataram	Which dataram used
+ * @param offset	The offset to OneNAND Core
+ *
+ * Copy data from OneNAND Core to DataRAM (read)
+ * Copy data from DataRAM to OneNAND Core (write)
+ * Erase the OneNAND Core (erase)
+ */
+static void onenand_data_handle(struct onenand_chip *this, int cmd,
+				int dataram, unsigned int offset)
+{
+	struct mtd_info *mtd = &info->mtd;
+	struct onenand_flash *flash = this->priv;
+	int main_offset, spare_offset;
+	void __iomem *src;
+	void __iomem *dest;
+	unsigned int i;
+
+	if (dataram) {
+		main_offset = mtd->writesize;
+		spare_offset = mtd->oobsize;
+	} else {
+		main_offset = 0;
+		spare_offset = 0;
+	}
+
+	switch (cmd) {
+	case ONENAND_CMD_READ:
+		src = ONENAND_CORE(flash) + offset;
+		dest = ONENAND_MAIN_AREA(this, main_offset);
+		memcpy(dest, src, mtd->writesize);
+		/* Fall through */
+
+	case ONENAND_CMD_READOOB:
+		src = ONENAND_CORE_SPARE(flash, this, offset);
+		dest = ONENAND_SPARE_AREA(this, spare_offset);
+		memcpy(dest, src, mtd->oobsize);
+		break;
+
+	case ONENAND_CMD_PROG:
+		src = ONENAND_MAIN_AREA(this, main_offset);
+		dest = ONENAND_CORE(flash) + offset;
+		/* To handle partial write */
+		for (i = 0; i < (1 << mtd->subpage_sft); i++) {
+			int off = i * this->subpagesize;
+			if (!memcmp(src + off, ffchars, this->subpagesize))
+				continue;
+			if (memcmp(dest + off, ffchars, this->subpagesize) &&
+			    onenand_check_overwrite(dest + off, src + off, this->subpagesize))
+				printk(KERN_ERR "over-write happend at 0x%08x\n", offset);
+			memcpy(dest + off, src + off, this->subpagesize);
+		}
+		/* Fall through */
+
+	case ONENAND_CMD_PROGOOB:
+		src = ONENAND_SPARE_AREA(this, spare_offset);
+		/* Check all data is 0xff chars */
+		if (!memcmp(src, ffchars, mtd->oobsize))
+			break;
+
+		dest = ONENAND_CORE_SPARE(flash, this, offset);
+		if (memcmp(dest, ffchars, mtd->oobsize) &&
+		    onenand_check_overwrite(dest, src, mtd->oobsize))
+			printk(KERN_ERR "OOB: over-write happend at 0x%08x\n",
+			       offset);
+		memcpy(dest, src, mtd->oobsize);
+		break;
+
+	case ONENAND_CMD_ERASE:
+		memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
+		memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
+		       (mtd->erasesize >> 5));
+		break;
+
+	default:
+		break;
+	}
+}
+
+/**
+ * onenand_command_handle - Handle command
+ * @param this		OneNAND device structure
+ * @param cmd		The command to be sent
+ *
+ * Emulate OneNAND command.
+ */
+static void onenand_command_handle(struct onenand_chip *this, int cmd)
+{
+	unsigned long offset = 0;
+	int block = -1, page = -1, bufferram = -1;
+	int dataram = 0;
+
+	switch (cmd) {
+	case ONENAND_CMD_UNLOCK:
+	case ONENAND_CMD_LOCK:
+	case ONENAND_CMD_LOCK_TIGHT:
+	case ONENAND_CMD_UNLOCK_ALL:
+		onenand_lock_handle(this, cmd);
+		break;
+
+	case ONENAND_CMD_BUFFERRAM:
+		/* Do nothing */
+		return;
+
+	default:
+		block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1);
+		if (block & (1 << ONENAND_DDP_SHIFT)) {
+			block &= ~(1 << ONENAND_DDP_SHIFT);
+			/* The half of chip block */
+			block += this->chipsize >> (this->erase_shift + 1);
+		}
+		if (cmd == ONENAND_CMD_ERASE)
+			break;
+
+		page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8);
+		page = (page >> ONENAND_FPA_SHIFT);
+		bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER);
+		bufferram >>= ONENAND_BSA_SHIFT;
+		bufferram &= ONENAND_BSA_DATARAM1;
+		dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0;
+		break;
+	}
+
+	if (block != -1)
+		offset += block << this->erase_shift;
+
+	if (page != -1)
+		offset += page << this->page_shift;
+
+	onenand_data_handle(this, cmd, dataram, offset);
+
+	onenand_update_interrupt(this, cmd);
+}
+
+/**
+ * onenand_writew - [OneNAND Interface] Emulate write operation
+ * @param value		value to write
+ * @param addr		address to write
+ *
+ * Write OneNAND register with value
+ */
+static void onenand_writew(unsigned short value, void __iomem * addr)
+{
+	struct onenand_chip *this = info->mtd.priv;
+
+	/* BootRAM handling */
+	if (addr < this->base + ONENAND_DATARAM) {
+		onenand_bootram_handle(this, value);
+		return;
+	}
+	/* Command handling */
+	if (addr == this->base + ONENAND_REG_COMMAND)
+		onenand_command_handle(this, value);
+
+	writew(value, addr);
+}
+
+/**
+ * flash_init - Initialize OneNAND simulator
+ * @param flash		OneNAND simulaotr data strucutres
+ *
+ * Initialize OneNAND simulator.
+ */
+static int __init flash_init(struct onenand_flash *flash)
+{
+	int density, size;
+	int buffer_size;
+
+	flash->base = kzalloc(131072, GFP_KERNEL);
+	if (!flash->base) {
+		printk(KERN_ERR "Unable to allocate base address.\n");
+		return -ENOMEM;
+	}
+
+	density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+	size = ((16 << 20) << density);
+
+	ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
+	if (!ONENAND_CORE(flash)) {
+		printk(KERN_ERR "Unable to allocate nand core address.\n");
+		kfree(flash->base);
+		return -ENOMEM;
+	}
+
+	memset(ONENAND_CORE(flash), 0xff, size + (size >> 5));
+
+	/* Setup registers */
+	writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
+	writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
+	writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
+
+	if (density < 2)
+		buffer_size = 0x0400;	/* 1KiB page */
+	else
+		buffer_size = 0x0800;	/* 2KiB page */
+	writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE);
+
+	return 0;
+}
+
+/**
+ * flash_exit - Clean up OneNAND simulator
+ * @param flash		OneNAND simulaotr data strucutres
+ *
+ * Clean up OneNAND simulator.
+ */
+static void flash_exit(struct onenand_flash *flash)
+{
+	vfree(ONENAND_CORE(flash));
+	kfree(flash->base);
+	kfree(flash);
+}
+
+static int __init onenand_sim_init(void)
+{
+	/* Allocate all 0xff chars pointer */
+	ffchars = kmalloc(MAX_ONENAND_PAGESIZE, GFP_KERNEL);
+	if (!ffchars) {
+		printk(KERN_ERR "Unable to allocate ff chars.\n");
+		return -ENOMEM;
+	}
+	memset(ffchars, 0xff, MAX_ONENAND_PAGESIZE);
+
+	/* Allocate OneNAND simulator mtd pointer */
+	info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
+	if (!info) {
+		printk(KERN_ERR "Unable to allocate core structures.\n");
+		kfree(ffchars);
+		return -ENOMEM;
+	}
+
+	/* Override write_word function */
+	info->onenand.write_word = onenand_writew;
+
+	if (flash_init(&info->flash)) {
+		printk(KERN_ERR "Unable to allocat flash.\n");
+		kfree(ffchars);
+		kfree(info);
+		return -ENOMEM;
+	}
+
+	info->parts = os_partitions;
+
+	info->onenand.base = info->flash.base;
+	info->onenand.priv = &info->flash;
+
+	info->mtd.name = "OneNAND simulator";
+	info->mtd.priv = &info->onenand;
+	info->mtd.owner = THIS_MODULE;
+
+	if (onenand_scan(&info->mtd, 1)) {
+		flash_exit(&info->flash);
+		kfree(ffchars);
+		kfree(info);
+		return -ENXIO;
+	}
+
+	add_mtd_partitions(&info->mtd, info->parts, ARRAY_SIZE(os_partitions));
+
+	return 0;
+}
+
+static void __exit onenand_sim_exit(void)
+{
+	struct onenand_chip *this = info->mtd.priv;
+	struct onenand_flash *flash = this->priv;
+
+	onenand_release(&info->mtd);
+	flash_exit(flash);
+	kfree(ffchars);
+	kfree(info);
+}
+
+module_init(onenand_sim_init);
+module_exit(onenand_sim_exit);
+
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("The OneNAND flash simulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index 006c03a..823fba4 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -779,10 +779,8 @@
 	else {
 		if (!mtd->erasesize) {
 			printk(KERN_WARNING PREFIX "please provide block_size");
-			kfree(part);
-			return;
-		}
-		else
+			goto out;
+		} else
 			part->block_size = mtd->erasesize;
 	}
 
@@ -804,7 +802,7 @@
 		if (!add_mtd_blktrans_dev((void*)part))
 			return;
 	}
-
+out:
 	kfree(part);
 }
 
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
index 94ee549..29c41ee 100644
--- a/drivers/mtd/ubi/scan.c
+++ b/drivers/mtd/ubi/scan.c
@@ -1314,11 +1314,10 @@
 	 * Make sure that all the physical eraseblocks are in one of the lists
 	 * or trees.
 	 */
-	buf = kmalloc(ubi->peb_count, GFP_KERNEL);
+	buf = kzalloc(ubi->peb_count, GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
 
-	memset(buf, 1, ubi->peb_count);
 	for (pnum = 0; pnum < ubi->peb_count; pnum++) {
 		err = ubi_io_is_bad(ubi, pnum);
 		if (err < 0) {
@@ -1326,28 +1325,28 @@
 			return err;
 		}
 		else if (err)
-			buf[pnum] = 0;
+			buf[pnum] = 1;
 	}
 
 	ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb)
 		ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)
-			buf[seb->pnum] = 0;
+			buf[seb->pnum] = 1;
 
 	list_for_each_entry(seb, &si->free, u.list)
-		buf[seb->pnum] = 0;
+		buf[seb->pnum] = 1;
 
 	list_for_each_entry(seb, &si->corr, u.list)
-		buf[seb->pnum] = 0;
+		buf[seb->pnum] = 1;
 
 	list_for_each_entry(seb, &si->erase, u.list)
-		buf[seb->pnum] = 0;
+		buf[seb->pnum] = 1;
 
 	list_for_each_entry(seb, &si->alien, u.list)
-		buf[seb->pnum] = 0;
+		buf[seb->pnum] = 1;
 
 	err = 0;
 	for (pnum = 0; pnum < ubi->peb_count; pnum++)
-		if (buf[pnum]) {
+		if (!buf[pnum]) {
 			ubi_err("PEB %d is not referred", pnum);
 			err = 1;
 		}
diff --git a/fs/Kconfig b/fs/Kconfig
index f9eed6d..bb02b39 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1225,6 +1225,14 @@
 	    - NOR flash with transparent ECC
 	    - DataFlash
 
+config JFFS2_FS_WBUF_VERIFY
+	bool "Verify JFFS2 write-buffer reads"
+	depends on JFFS2_FS_WRITEBUFFER
+	default n
+	help
+	  This causes JFFS2 to read back every page written through the
+	  write-buffer, and check for errors.
+
 config JFFS2_SUMMARY
 	bool "JFFS2 summary support (EXPERIMENTAL)"
 	depends on JFFS2_FS && EXPERIMENTAL
@@ -1295,52 +1303,71 @@
 	select ZLIB_DEFLATE
 	depends on JFFS2_FS
 	default y
-        help
-          Zlib is designed to be a free, general-purpose, legally unencumbered,
-          lossless data-compression library for use on virtually any computer
-          hardware and operating system. See <http://www.gzip.org/zlib/> for
-          further information.
+	help
+	  Zlib is designed to be a free, general-purpose, legally unencumbered,
+	  lossless data-compression library for use on virtually any computer
+	  hardware and operating system. See <http://www.gzip.org/zlib/> for
+	  further information.
 
-          Say 'Y' if unsure.
+	  Say 'Y' if unsure.
+
+config JFFS2_LZO
+	bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS
+	select LZO_COMPRESS
+	select LZO_DECOMPRESS
+	depends on JFFS2_FS
+	default n
+	help
+	  minilzo-based compression. Generally works better than Zlib.
+
+	  This feature was added in July, 2007. Say 'N' if you need
+	  compatibility with older bootloaders or kernels.
 
 config JFFS2_RTIME
 	bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
 	depends on JFFS2_FS
 	default y
-        help
-          Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
+	help
+	  Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
 
 config JFFS2_RUBIN
 	bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS
 	depends on JFFS2_FS
 	default n
-        help
-          RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
+	help
+	  RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
 
 choice
-        prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
-        default JFFS2_CMODE_PRIORITY
-        depends on JFFS2_FS
-        help
-          You can set here the default compression mode of JFFS2 from
-          the available compression modes. Don't touch if unsure.
+	prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
+	default JFFS2_CMODE_PRIORITY
+	depends on JFFS2_FS
+	help
+	  You can set here the default compression mode of JFFS2 from
+	  the available compression modes. Don't touch if unsure.
 
 config JFFS2_CMODE_NONE
-        bool "no compression"
-        help
-          Uses no compression.
+	bool "no compression"
+	help
+	  Uses no compression.
 
 config JFFS2_CMODE_PRIORITY
-        bool "priority"
-        help
-          Tries the compressors in a predefined order and chooses the first
-          successful one.
+	bool "priority"
+	help
+	  Tries the compressors in a predefined order and chooses the first
+	  successful one.
 
 config JFFS2_CMODE_SIZE
-        bool "size (EXPERIMENTAL)"
-        help
-          Tries all compressors and chooses the one which has the smallest
-          result.
+	bool "size (EXPERIMENTAL)"
+	help
+	  Tries all compressors and chooses the one which has the smallest
+	  result.
+
+config JFFS2_CMODE_FAVOURLZO
+	bool "Favour LZO"
+	help
+	  Tries all compressors and chooses the one which has the smallest
+	  result but gives some preference to LZO (which has faster
+	  decompression) at the expense of size.
 
 endchoice
 
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
index c32b241..60e5d49 100644
--- a/fs/jffs2/Makefile
+++ b/fs/jffs2/Makefile
@@ -17,4 +17,5 @@
 jffs2-$(CONFIG_JFFS2_RUBIN)	+= compr_rubin.o
 jffs2-$(CONFIG_JFFS2_RTIME)	+= compr_rtime.o
 jffs2-$(CONFIG_JFFS2_ZLIB)	+= compr_zlib.o
+jffs2-$(CONFIG_JFFS2_LZO)	+= compr_lzo.o
 jffs2-$(CONFIG_JFFS2_SUMMARY)   += summary.o
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 65b3a1b..8ec9323 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -176,7 +176,7 @@
 	spin_unlock(&inode->i_lock);
 }
 
-static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
+struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
 {
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct posix_acl *acl;
@@ -247,8 +247,13 @@
 			if (rc < 0)
 				return rc;
 			if (inode->i_mode != mode) {
-				inode->i_mode = mode;
-				jffs2_dirty_inode(inode);
+				struct iattr attr;
+
+				attr.ia_valid = ATTR_MODE;
+				attr.ia_mode = mode;
+				rc = jffs2_do_setattr(inode, &attr);
+				if (rc < 0)
+					return rc;
 			}
 			if (rc == 0)
 				acl = NULL;
@@ -307,22 +312,16 @@
 	return generic_permission(inode, mask, jffs2_check_acl);
 }
 
-int jffs2_init_acl(struct inode *inode, struct inode *dir)
+int jffs2_init_acl(struct inode *inode, struct posix_acl *acl)
 {
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-	struct posix_acl *acl = NULL, *clone;
+	struct posix_acl *clone;
 	mode_t mode;
 	int rc = 0;
 
 	f->i_acl_access = JFFS2_ACL_NOT_CACHED;
 	f->i_acl_default = JFFS2_ACL_NOT_CACHED;
-	if (!S_ISLNK(inode->i_mode)) {
-		acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
-		if (IS_ERR(acl))
-			return PTR_ERR(acl);
-		if (!acl)
-			inode->i_mode &= ~current->fs->umask;
-	}
+
 	if (acl) {
 		if (S_ISDIR(inode->i_mode)) {
 			rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
index c84378c..90a2dbf 100644
--- a/fs/jffs2/acl.h
+++ b/fs/jffs2/acl.h
@@ -28,9 +28,10 @@
 
 #define JFFS2_ACL_NOT_CACHED ((void *)-1)
 
+extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
 extern int jffs2_permission(struct inode *, int, struct nameidata *);
 extern int jffs2_acl_chmod(struct inode *);
-extern int jffs2_init_acl(struct inode *, struct inode *);
+extern int jffs2_init_acl(struct inode *, struct posix_acl *);
 extern void jffs2_clear_acl(struct jffs2_inode_info *);
 
 extern struct xattr_handler jffs2_acl_access_xattr_handler;
@@ -38,6 +39,7 @@
 
 #else
 
+#define jffs2_get_acl(inode, type)	(NULL)
 #define jffs2_permission NULL
 #define jffs2_acl_chmod(inode)		(0)
 #define jffs2_init_acl(inode,dir)	(0)
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c
index 504643f..d568ae8 100644
--- a/fs/jffs2/background.c
+++ b/fs/jffs2/background.c
@@ -23,8 +23,8 @@
 void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
 {
 	spin_lock(&c->erase_completion_lock);
-        if (c->gc_task && jffs2_thread_should_wake(c))
-                send_sig(SIGHUP, c->gc_task, 1);
+	if (c->gc_task && jffs2_thread_should_wake(c))
+		send_sig(SIGHUP, c->gc_task, 1);
 	spin_unlock(&c->erase_completion_lock);
 }
 
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
index 0ca2fff..722a6b6 100644
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -285,6 +285,14 @@
 	   than actually making progress? */
 	c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
 
+	/* What number of 'very dirty' eraseblocks do we allow before we
+	   trigger the GC thread even if we don't _need_ the space. When we
+	   can't mark nodes obsolete on the medium, the old dirty nodes cause
+	   performance problems because we have to inspect and discard them. */
+	c->vdirty_blocks_gctrigger = c->resv_blocks_gctrigger;
+	if (jffs2_can_mark_obsolete(c))
+		c->vdirty_blocks_gctrigger *= 10;
+
 	/* If there's less than this amount of dirty space, don't bother
 	   trying to GC to make more space. It'll be a fruitless task */
 	c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
@@ -303,6 +311,8 @@
 		  c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024);
 	dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n",
 		  c->nospc_dirty_size);
+	dbg_fsbuild("Very dirty blocks before GC triggered: %d\n",
+		  c->vdirty_blocks_gctrigger);
 }
 
 int jffs2_do_mount_fs(struct jffs2_sb_info *c)
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
index 485d065..86739ee 100644
--- a/fs/jffs2/compr.c
+++ b/fs/jffs2/compr.c
@@ -5,7 +5,7 @@
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
  * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
- *                    University of Szeged, Hungary
+ *		    University of Szeged, Hungary
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
@@ -24,6 +24,34 @@
 /* Statistics for blocks stored without compression */
 static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
 
+
+/*
+ * Return 1 to use this compression
+ */
+static int jffs2_is_best_compression(struct jffs2_compressor *this,
+		struct jffs2_compressor *best, uint32_t size, uint32_t bestsize)
+{
+	switch (jffs2_compression_mode) {
+	case JFFS2_COMPR_MODE_SIZE:
+		if (bestsize > size)
+			return 1;
+		return 0;
+	case JFFS2_COMPR_MODE_FAVOURLZO:
+		if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size))
+			return 1;
+		if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size))
+			return 1;
+		if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100)))
+			return 1;
+		if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size)
+			return 1;
+
+		return 0;
+	}
+	/* Shouldn't happen */
+	return 0;
+}
+
 /* jffs2_compress:
  * @data: Pointer to uncompressed data
  * @cdata: Pointer to returned pointer to buffer for compressed data
@@ -43,121 +71,124 @@
  * *datalen accordingly to show the amount of data which were compressed.
  */
 uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-			     unsigned char *data_in, unsigned char **cpage_out,
-			     uint32_t *datalen, uint32_t *cdatalen)
+			unsigned char *data_in, unsigned char **cpage_out,
+			uint32_t *datalen, uint32_t *cdatalen)
 {
 	int ret = JFFS2_COMPR_NONE;
-        int compr_ret;
-        struct jffs2_compressor *this, *best=NULL;
-        unsigned char *output_buf = NULL, *tmp_buf;
-        uint32_t orig_slen, orig_dlen;
-        uint32_t best_slen=0, best_dlen=0;
+	int compr_ret;
+	struct jffs2_compressor *this, *best=NULL;
+	unsigned char *output_buf = NULL, *tmp_buf;
+	uint32_t orig_slen, orig_dlen;
+	uint32_t best_slen=0, best_dlen=0;
 
-        switch (jffs2_compression_mode) {
-        case JFFS2_COMPR_MODE_NONE:
-                break;
-        case JFFS2_COMPR_MODE_PRIORITY:
-                output_buf = kmalloc(*cdatalen,GFP_KERNEL);
-                if (!output_buf) {
-                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
-                        goto out;
-                }
-                orig_slen = *datalen;
-                orig_dlen = *cdatalen;
-                spin_lock(&jffs2_compressor_list_lock);
-                list_for_each_entry(this, &jffs2_compressor_list, list) {
-                        /* Skip decompress-only backwards-compatibility and disabled modules */
-                        if ((!this->compress)||(this->disabled))
-                                continue;
+	switch (jffs2_compression_mode) {
+	case JFFS2_COMPR_MODE_NONE:
+		break;
+	case JFFS2_COMPR_MODE_PRIORITY:
+		output_buf = kmalloc(*cdatalen,GFP_KERNEL);
+		if (!output_buf) {
+			printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
+			goto out;
+		}
+		orig_slen = *datalen;
+		orig_dlen = *cdatalen;
+		spin_lock(&jffs2_compressor_list_lock);
+		list_for_each_entry(this, &jffs2_compressor_list, list) {
+			/* Skip decompress-only backwards-compatibility and disabled modules */
+			if ((!this->compress)||(this->disabled))
+				continue;
 
-                        this->usecount++;
-                        spin_unlock(&jffs2_compressor_list_lock);
-                        *datalen  = orig_slen;
-                        *cdatalen = orig_dlen;
-                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
-                        spin_lock(&jffs2_compressor_list_lock);
-                        this->usecount--;
-                        if (!compr_ret) {
-                                ret = this->compr;
-                                this->stat_compr_blocks++;
-                                this->stat_compr_orig_size += *datalen;
-                                this->stat_compr_new_size  += *cdatalen;
-                                break;
-                        }
-                }
-                spin_unlock(&jffs2_compressor_list_lock);
-                if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
-                break;
-        case JFFS2_COMPR_MODE_SIZE:
-                orig_slen = *datalen;
-                orig_dlen = *cdatalen;
-                spin_lock(&jffs2_compressor_list_lock);
-                list_for_each_entry(this, &jffs2_compressor_list, list) {
-                        /* Skip decompress-only backwards-compatibility and disabled modules */
-                        if ((!this->compress)||(this->disabled))
-                                continue;
-                        /* Allocating memory for output buffer if necessary */
-                        if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
-                                spin_unlock(&jffs2_compressor_list_lock);
-                                kfree(this->compr_buf);
-                                spin_lock(&jffs2_compressor_list_lock);
-                                this->compr_buf_size=0;
-                                this->compr_buf=NULL;
-                        }
-                        if (!this->compr_buf) {
-                                spin_unlock(&jffs2_compressor_list_lock);
-                                tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
-                                spin_lock(&jffs2_compressor_list_lock);
-                                if (!tmp_buf) {
-                                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
-                                        continue;
-                                }
-                                else {
-                                        this->compr_buf = tmp_buf;
-                                        this->compr_buf_size = orig_dlen;
-                                }
-                        }
-                        this->usecount++;
-                        spin_unlock(&jffs2_compressor_list_lock);
-                        *datalen  = orig_slen;
-                        *cdatalen = orig_dlen;
-                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
-                        spin_lock(&jffs2_compressor_list_lock);
-                        this->usecount--;
-                        if (!compr_ret) {
-                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
-                                        best_dlen = *cdatalen;
-                                        best_slen = *datalen;
-                                        best = this;
-                                }
-                        }
-                }
-                if (best_dlen) {
-                        *cdatalen = best_dlen;
-                        *datalen  = best_slen;
-                        output_buf = best->compr_buf;
-                        best->compr_buf = NULL;
-                        best->compr_buf_size = 0;
-                        best->stat_compr_blocks++;
-                        best->stat_compr_orig_size += best_slen;
-                        best->stat_compr_new_size  += best_dlen;
-                        ret = best->compr;
-                }
-                spin_unlock(&jffs2_compressor_list_lock);
-                break;
-        default:
-                printk(KERN_ERR "JFFS2: unknow compression mode.\n");
-        }
+			this->usecount++;
+			spin_unlock(&jffs2_compressor_list_lock);
+			*datalen  = orig_slen;
+			*cdatalen = orig_dlen;
+			compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+			spin_lock(&jffs2_compressor_list_lock);
+			this->usecount--;
+			if (!compr_ret) {
+				ret = this->compr;
+				this->stat_compr_blocks++;
+				this->stat_compr_orig_size += *datalen;
+				this->stat_compr_new_size  += *cdatalen;
+				break;
+			}
+		}
+		spin_unlock(&jffs2_compressor_list_lock);
+		if (ret == JFFS2_COMPR_NONE)
+			kfree(output_buf);
+		break;
+	case JFFS2_COMPR_MODE_SIZE:
+	case JFFS2_COMPR_MODE_FAVOURLZO:
+		orig_slen = *datalen;
+		orig_dlen = *cdatalen;
+		spin_lock(&jffs2_compressor_list_lock);
+		list_for_each_entry(this, &jffs2_compressor_list, list) {
+			/* Skip decompress-only backwards-compatibility and disabled modules */
+			if ((!this->compress)||(this->disabled))
+				continue;
+			/* Allocating memory for output buffer if necessary */
+			if ((this->compr_buf_size < orig_slen) && (this->compr_buf)) {
+				spin_unlock(&jffs2_compressor_list_lock);
+				kfree(this->compr_buf);
+				spin_lock(&jffs2_compressor_list_lock);
+				this->compr_buf_size=0;
+				this->compr_buf=NULL;
+			}
+			if (!this->compr_buf) {
+				spin_unlock(&jffs2_compressor_list_lock);
+				tmp_buf = kmalloc(orig_slen, GFP_KERNEL);
+				spin_lock(&jffs2_compressor_list_lock);
+				if (!tmp_buf) {
+					printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n", orig_slen);
+					continue;
+				}
+				else {
+					this->compr_buf = tmp_buf;
+					this->compr_buf_size = orig_slen;
+				}
+			}
+			this->usecount++;
+			spin_unlock(&jffs2_compressor_list_lock);
+			*datalen  = orig_slen;
+			*cdatalen = orig_dlen;
+			compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+			spin_lock(&jffs2_compressor_list_lock);
+			this->usecount--;
+			if (!compr_ret) {
+				if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen))
+						&& (*cdatalen < *datalen)) {
+					best_dlen = *cdatalen;
+					best_slen = *datalen;
+					best = this;
+				}
+			}
+		}
+		if (best_dlen) {
+			*cdatalen = best_dlen;
+			*datalen  = best_slen;
+			output_buf = best->compr_buf;
+			best->compr_buf = NULL;
+			best->compr_buf_size = 0;
+			best->stat_compr_blocks++;
+			best->stat_compr_orig_size += best_slen;
+			best->stat_compr_new_size  += best_dlen;
+			ret = best->compr;
+		}
+		spin_unlock(&jffs2_compressor_list_lock);
+		break;
+	default:
+		printk(KERN_ERR "JFFS2: unknow compression mode.\n");
+	}
  out:
-        if (ret == JFFS2_COMPR_NONE) {
-	        *cpage_out = data_in;
-	        *datalen = *cdatalen;
-                none_stat_compr_blocks++;
-                none_stat_compr_size += *datalen;
-        }
-        else {
-                *cpage_out = output_buf;
-        }
+	if (ret == JFFS2_COMPR_NONE) {
+		*cpage_out = data_in;
+		*datalen = *cdatalen;
+		none_stat_compr_blocks++;
+		none_stat_compr_size += *datalen;
+	}
+	else {
+		*cpage_out = output_buf;
+	}
 	return ret;
 }
 
@@ -165,8 +196,8 @@
 		     uint16_t comprtype, unsigned char *cdata_in,
 		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
 {
-        struct jffs2_compressor *this;
-        int ret;
+	struct jffs2_compressor *this;
+	int ret;
 
 	/* Older code had a bug where it would write non-zero 'usercompr'
 	   fields. Deal with it. */
@@ -177,32 +208,32 @@
 	case JFFS2_COMPR_NONE:
 		/* This should be special-cased elsewhere, but we might as well deal with it */
 		memcpy(data_out, cdata_in, datalen);
-                none_stat_decompr_blocks++;
+		none_stat_decompr_blocks++;
 		break;
 	case JFFS2_COMPR_ZERO:
 		memset(data_out, 0, datalen);
 		break;
 	default:
-                spin_lock(&jffs2_compressor_list_lock);
-                list_for_each_entry(this, &jffs2_compressor_list, list) {
-                        if (comprtype == this->compr) {
-                                this->usecount++;
-                                spin_unlock(&jffs2_compressor_list_lock);
-                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
-                                spin_lock(&jffs2_compressor_list_lock);
-                                if (ret) {
-                                        printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
-                                }
-                                else {
-                                        this->stat_decompr_blocks++;
-                                }
-                                this->usecount--;
-                                spin_unlock(&jffs2_compressor_list_lock);
-                                return ret;
-                        }
-                }
+		spin_lock(&jffs2_compressor_list_lock);
+		list_for_each_entry(this, &jffs2_compressor_list, list) {
+			if (comprtype == this->compr) {
+				this->usecount++;
+				spin_unlock(&jffs2_compressor_list_lock);
+				ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
+				spin_lock(&jffs2_compressor_list_lock);
+				if (ret) {
+					printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
+				}
+				else {
+					this->stat_decompr_blocks++;
+				}
+				this->usecount--;
+				spin_unlock(&jffs2_compressor_list_lock);
+				return ret;
+			}
+		}
 		printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
-                spin_unlock(&jffs2_compressor_list_lock);
+		spin_unlock(&jffs2_compressor_list_lock);
 		return -EIO;
 	}
 	return 0;
@@ -210,108 +241,119 @@
 
 int jffs2_register_compressor(struct jffs2_compressor *comp)
 {
-        struct jffs2_compressor *this;
+	struct jffs2_compressor *this;
 
-        if (!comp->name) {
-                printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
-                return -1;
-        }
-        comp->compr_buf_size=0;
-        comp->compr_buf=NULL;
-        comp->usecount=0;
-        comp->stat_compr_orig_size=0;
-        comp->stat_compr_new_size=0;
-        comp->stat_compr_blocks=0;
-        comp->stat_decompr_blocks=0;
-        D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
+	if (!comp->name) {
+		printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
+		return -1;
+	}
+	comp->compr_buf_size=0;
+	comp->compr_buf=NULL;
+	comp->usecount=0;
+	comp->stat_compr_orig_size=0;
+	comp->stat_compr_new_size=0;
+	comp->stat_compr_blocks=0;
+	comp->stat_decompr_blocks=0;
+	D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
 
-        spin_lock(&jffs2_compressor_list_lock);
+	spin_lock(&jffs2_compressor_list_lock);
 
-        list_for_each_entry(this, &jffs2_compressor_list, list) {
-                if (this->priority < comp->priority) {
-                        list_add(&comp->list, this->list.prev);
-                        goto out;
-                }
-        }
-        list_add_tail(&comp->list, &jffs2_compressor_list);
+	list_for_each_entry(this, &jffs2_compressor_list, list) {
+		if (this->priority < comp->priority) {
+			list_add(&comp->list, this->list.prev);
+			goto out;
+		}
+	}
+	list_add_tail(&comp->list, &jffs2_compressor_list);
 out:
-        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
-                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
-        })
+	D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+		printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+	})
 
-        spin_unlock(&jffs2_compressor_list_lock);
+	spin_unlock(&jffs2_compressor_list_lock);
 
-        return 0;
+	return 0;
 }
 
 int jffs2_unregister_compressor(struct jffs2_compressor *comp)
 {
-        D2(struct jffs2_compressor *this;)
+	D2(struct jffs2_compressor *this;)
 
-        D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
+	D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
 
-        spin_lock(&jffs2_compressor_list_lock);
+	spin_lock(&jffs2_compressor_list_lock);
 
-        if (comp->usecount) {
-                spin_unlock(&jffs2_compressor_list_lock);
-                printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
-                return -1;
-        }
-        list_del(&comp->list);
+	if (comp->usecount) {
+		spin_unlock(&jffs2_compressor_list_lock);
+		printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
+		return -1;
+	}
+	list_del(&comp->list);
 
-        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
-                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
-        })
-        spin_unlock(&jffs2_compressor_list_lock);
-        return 0;
+	D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+		printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+	})
+	spin_unlock(&jffs2_compressor_list_lock);
+	return 0;
 }
 
 void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
 {
-        if (orig != comprbuf)
-                kfree(comprbuf);
+	if (orig != comprbuf)
+		kfree(comprbuf);
 }
 
 int __init jffs2_compressors_init(void)
 {
 /* Registering compressors */
 #ifdef CONFIG_JFFS2_ZLIB
-        jffs2_zlib_init();
+	jffs2_zlib_init();
 #endif
 #ifdef CONFIG_JFFS2_RTIME
-        jffs2_rtime_init();
+	jffs2_rtime_init();
 #endif
 #ifdef CONFIG_JFFS2_RUBIN
-        jffs2_rubinmips_init();
-        jffs2_dynrubin_init();
+	jffs2_rubinmips_init();
+	jffs2_dynrubin_init();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+	jffs2_lzo_init();
 #endif
 /* Setting default compression mode */
 #ifdef CONFIG_JFFS2_CMODE_NONE
-        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
-        D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
+	jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+	D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
 #else
 #ifdef CONFIG_JFFS2_CMODE_SIZE
-        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
-        D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
+	jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+	D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
 #else
-        D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
+#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
+	jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO;
+	D1(printk(KERN_INFO "JFFS2: default compression mode: favourlzo\n");)
+#else
+	D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
 #endif
 #endif
-        return 0;
+#endif
+	return 0;
 }
 
 int jffs2_compressors_exit(void)
 {
 /* Unregistering compressors */
+#ifdef CONFIG_JFFS2_LZO
+	jffs2_lzo_exit();
+#endif
 #ifdef CONFIG_JFFS2_RUBIN
-        jffs2_dynrubin_exit();
-        jffs2_rubinmips_exit();
+	jffs2_dynrubin_exit();
+	jffs2_rubinmips_exit();
 #endif
 #ifdef CONFIG_JFFS2_RTIME
-        jffs2_rtime_exit();
+	jffs2_rtime_exit();
 #endif
 #ifdef CONFIG_JFFS2_ZLIB
-        jffs2_zlib_exit();
+	jffs2_zlib_exit();
 #endif
-        return 0;
+	return 0;
 }
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h
index 68cc701..7d1d72f 100644
--- a/fs/jffs2/compr.h
+++ b/fs/jffs2/compr.h
@@ -2,7 +2,7 @@
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
  * Copyright © 2004   Ferenc Havasi <havasi@inf.u-szeged.hu>,
- *                    University of Szeged, Hungary
+ *		      University of Szeged, Hungary
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
@@ -27,34 +27,38 @@
 #define JFFS2_RUBINMIPS_PRIORITY 10
 #define JFFS2_DYNRUBIN_PRIORITY  20
 #define JFFS2_LZARI_PRIORITY     30
-#define JFFS2_LZO_PRIORITY       40
 #define JFFS2_RTIME_PRIORITY     50
 #define JFFS2_ZLIB_PRIORITY      60
+#define JFFS2_LZO_PRIORITY       80
+
 
 #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
-#define JFFS2_DYNRUBIN_DISABLED  /*        for decompression */
+#define JFFS2_DYNRUBIN_DISABLED  /*	   for decompression */
 
 #define JFFS2_COMPR_MODE_NONE       0
 #define JFFS2_COMPR_MODE_PRIORITY   1
 #define JFFS2_COMPR_MODE_SIZE       2
+#define JFFS2_COMPR_MODE_FAVOURLZO  3
+
+#define FAVOUR_LZO_PERCENT 80
 
 struct jffs2_compressor {
-        struct list_head list;
-        int priority;              /* used by prirority comr. mode */
-        char *name;
-        char compr;                /* JFFS2_COMPR_XXX */
-        int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
-                        uint32_t *srclen, uint32_t *destlen, void *model);
-        int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
-                        uint32_t cdatalen, uint32_t datalen, void *model);
-        int usecount;
-        int disabled;              /* if seted the compressor won't compress */
-        unsigned char *compr_buf;  /* used by size compr. mode */
-        uint32_t compr_buf_size;   /* used by size compr. mode */
-        uint32_t stat_compr_orig_size;
-        uint32_t stat_compr_new_size;
-        uint32_t stat_compr_blocks;
-        uint32_t stat_decompr_blocks;
+	struct list_head list;
+	int priority;			/* used by prirority comr. mode */
+	char *name;
+	char compr;			/* JFFS2_COMPR_XXX */
+	int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
+			uint32_t *srclen, uint32_t *destlen, void *model);
+	int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
+			  uint32_t cdatalen, uint32_t datalen, void *model);
+	int usecount;
+	int disabled;		/* if set the compressor won't compress */
+	unsigned char *compr_buf;	/* used by size compr. mode */
+	uint32_t compr_buf_size;	/* used by size compr. mode */
+	uint32_t stat_compr_orig_size;
+	uint32_t stat_compr_new_size;
+	uint32_t stat_compr_blocks;
+	uint32_t stat_decompr_blocks;
 };
 
 int jffs2_register_compressor(struct jffs2_compressor *comp);
@@ -64,12 +68,12 @@
 int jffs2_compressors_exit(void);
 
 uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-                             unsigned char *data_in, unsigned char **cpage_out,
-                             uint32_t *datalen, uint32_t *cdatalen);
+			unsigned char *data_in, unsigned char **cpage_out,
+			uint32_t *datalen, uint32_t *cdatalen);
 
 int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-                     uint16_t comprtype, unsigned char *cdata_in,
-                     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+		     uint16_t comprtype, unsigned char *cdata_in,
+		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
 
 void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
 
@@ -90,5 +94,9 @@
 int jffs2_zlib_init(void);
 void jffs2_zlib_exit(void);
 #endif
+#ifdef CONFIG_JFFS2_LZO
+int jffs2_lzo_init(void);
+void jffs2_lzo_exit(void);
+#endif
 
 #endif /* __JFFS2_COMPR_H__ */
diff --git a/fs/jffs2/compr_lzo.c b/fs/jffs2/compr_lzo.c
new file mode 100644
index 0000000..47b0457
--- /dev/null
+++ b/fs/jffs2/compr_lzo.c
@@ -0,0 +1,108 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright © 2007 Nokia Corporation. All rights reserved.
+ *
+ * Created by Richard Purdie <rpurdie@openedhand.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/lzo.h>
+#include "compr.h"
+
+static void *lzo_mem;
+static void *lzo_compress_buf;
+static DEFINE_MUTEX(deflate_mutex);
+
+static void free_workspace(void)
+{
+	vfree(lzo_mem);
+	vfree(lzo_compress_buf);
+}
+
+static int __init alloc_workspace(void)
+{
+	lzo_mem = vmalloc(LZO1X_MEM_COMPRESS);
+	lzo_compress_buf = vmalloc(lzo1x_worst_compress(PAGE_SIZE));
+
+	if (!lzo_mem || !lzo_compress_buf) {
+		printk(KERN_WARNING "Failed to allocate lzo deflate workspace\n");
+		free_workspace();
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
+			      uint32_t *sourcelen, uint32_t *dstlen, void *model)
+{
+	size_t compress_size;
+	int ret;
+
+	mutex_lock(&deflate_mutex);
+	ret = lzo1x_1_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem);
+	mutex_unlock(&deflate_mutex);
+
+	if (ret != LZO_E_OK)
+		return -1;
+
+	if (compress_size > *dstlen)
+		return -1;
+
+	memcpy(cpage_out, lzo_compress_buf, compress_size);
+	*dstlen = compress_size;
+
+	return 0;
+}
+
+static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
+				 uint32_t srclen, uint32_t destlen, void *model)
+{
+	size_t dl = destlen;
+	int ret;
+
+	ret = lzo1x_decompress_safe(data_in, srclen, cpage_out, &dl);
+
+	if (ret != LZO_E_OK || dl != destlen)
+		return -1;
+
+	return 0;
+}
+
+static struct jffs2_compressor jffs2_lzo_comp = {
+	.priority = JFFS2_LZO_PRIORITY,
+	.name = "lzo",
+	.compr = JFFS2_COMPR_LZO,
+	.compress = &jffs2_lzo_compress,
+	.decompress = &jffs2_lzo_decompress,
+	.disabled = 0,
+};
+
+int __init jffs2_lzo_init(void)
+{
+	int ret;
+
+	ret = alloc_workspace();
+	if (ret < 0)
+		return ret;
+
+	ret = jffs2_register_compressor(&jffs2_lzo_comp);
+	if (ret)
+		free_workspace();
+
+	return ret;
+}
+
+void jffs2_lzo_exit(void)
+{
+	jffs2_unregister_compressor(&jffs2_lzo_comp);
+	free_workspace();
+}
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c
index 0d0bfd2..546d153 100644
--- a/fs/jffs2/compr_rtime.c
+++ b/fs/jffs2/compr_rtime.c
@@ -104,7 +104,7 @@
 			}
 		}
 	}
-        return 0;
+	return 0;
 }
 
 static struct jffs2_compressor jffs2_rtime_comp = {
diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c
index ea0431e..c73fa89 100644
--- a/fs/jffs2/compr_rubin.c
+++ b/fs/jffs2/compr_rubin.c
@@ -384,7 +384,7 @@
 				      void *model)
 {
 	rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
-        return 0;
+	return 0;
 }
 
 static int jffs2_dynrubin_decompress(unsigned char *data_in,
@@ -399,7 +399,7 @@
 		bits[c] = data_in[c];
 
 	rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
-        return 0;
+	return 0;
 }
 
 static struct jffs2_compressor jffs2_rubinmips_comp = {
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c
index 2b87fcc..cfd301a 100644
--- a/fs/jffs2/compr_zlib.c
+++ b/fs/jffs2/compr_zlib.c
@@ -181,7 +181,7 @@
 	}
 	zlib_inflateEnd(&inf_strm);
 	mutex_unlock(&inflate_mutex);
-        return 0;
+	return 0;
 }
 
 static struct jffs2_compressor jffs2_zlib_comp = {
@@ -203,11 +203,11 @@
 
     ret = alloc_workspaces();
     if (ret)
-        return ret;
+	    return ret;
 
     ret = jffs2_register_compressor(&jffs2_zlib_comp);
     if (ret)
-        free_workspaces();
+	    free_workspaces();
 
     return ret;
 }
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index c1dfca3..8353eb9 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -32,7 +32,7 @@
 static int jffs2_rmdir (struct inode *,struct dentry *);
 static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t);
 static int jffs2_rename (struct inode *, struct dentry *,
-                        struct inode *, struct dentry *);
+			 struct inode *, struct dentry *);
 
 const struct file_operations jffs2_dir_operations =
 {
@@ -182,6 +182,7 @@
 	struct jffs2_inode_info *f, *dir_f;
 	struct jffs2_sb_info *c;
 	struct inode *inode;
+	struct posix_acl *acl;
 	int ret;
 
 	ri = jffs2_alloc_raw_inode();
@@ -192,7 +193,7 @@
 
 	D1(printk(KERN_DEBUG "jffs2_create()\n"));
 
-	inode = jffs2_new_inode(dir_i, mode, ri);
+	inode = jffs2_new_inode(dir_i, mode, ri, &acl);
 
 	if (IS_ERR(inode)) {
 		D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
@@ -212,12 +213,12 @@
 			      dentry->d_name.name, dentry->d_name.len);
 
 	if (ret)
-		goto fail;
+		goto fail_acl;
 
 	ret = jffs2_init_security(inode, dir_i);
 	if (ret)
-		goto fail;
-	ret = jffs2_init_acl(inode, dir_i);
+		goto fail_acl;
+	ret = jffs2_init_acl(inode, acl);
 	if (ret)
 		goto fail;
 
@@ -230,6 +231,8 @@
 		  inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
 	return 0;
 
+ fail_acl:
+	posix_acl_release(acl);
  fail:
 	make_bad_inode(inode);
 	iput(inode);
@@ -306,6 +309,7 @@
 	struct jffs2_full_dirent *fd;
 	int namelen;
 	uint32_t alloclen;
+	struct posix_acl *acl;
 	int ret, targetlen = strlen(target);
 
 	/* FIXME: If you care. We'd need to use frags for the target
@@ -332,7 +336,7 @@
 		return ret;
 	}
 
-	inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri);
+	inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri, &acl);
 
 	if (IS_ERR(inode)) {
 		jffs2_free_raw_inode(ri);
@@ -362,6 +366,7 @@
 		up(&f->sem);
 		jffs2_complete_reservation(c);
 		jffs2_clear_inode(inode);
+		posix_acl_release(acl);
 		return PTR_ERR(fn);
 	}
 
@@ -372,6 +377,7 @@
 		up(&f->sem);
 		jffs2_complete_reservation(c);
 		jffs2_clear_inode(inode);
+		posix_acl_release(acl);
 		return -ENOMEM;
 	}
 
@@ -389,9 +395,10 @@
 	ret = jffs2_init_security(inode, dir_i);
 	if (ret) {
 		jffs2_clear_inode(inode);
+		posix_acl_release(acl);
 		return ret;
 	}
-	ret = jffs2_init_acl(inode, dir_i);
+	ret = jffs2_init_acl(inode, acl);
 	if (ret) {
 		jffs2_clear_inode(inode);
 		return ret;
@@ -469,6 +476,7 @@
 	struct jffs2_full_dirent *fd;
 	int namelen;
 	uint32_t alloclen;
+	struct posix_acl *acl;
 	int ret;
 
 	mode |= S_IFDIR;
@@ -491,7 +499,7 @@
 		return ret;
 	}
 
-	inode = jffs2_new_inode(dir_i, mode, ri);
+	inode = jffs2_new_inode(dir_i, mode, ri, &acl);
 
 	if (IS_ERR(inode)) {
 		jffs2_free_raw_inode(ri);
@@ -518,6 +526,7 @@
 		up(&f->sem);
 		jffs2_complete_reservation(c);
 		jffs2_clear_inode(inode);
+		posix_acl_release(acl);
 		return PTR_ERR(fn);
 	}
 	/* No data here. Only a metadata node, which will be
@@ -531,9 +540,10 @@
 	ret = jffs2_init_security(inode, dir_i);
 	if (ret) {
 		jffs2_clear_inode(inode);
+		posix_acl_release(acl);
 		return ret;
 	}
-	ret = jffs2_init_acl(inode, dir_i);
+	ret = jffs2_init_acl(inode, acl);
 	if (ret) {
 		jffs2_clear_inode(inode);
 		return ret;
@@ -629,6 +639,7 @@
 	union jffs2_device_node dev;
 	int devlen = 0;
 	uint32_t alloclen;
+	struct posix_acl *acl;
 	int ret;
 
 	if (!new_valid_dev(rdev))
@@ -655,7 +666,7 @@
 		return ret;
 	}
 
-	inode = jffs2_new_inode(dir_i, mode, ri);
+	inode = jffs2_new_inode(dir_i, mode, ri, &acl);
 
 	if (IS_ERR(inode)) {
 		jffs2_free_raw_inode(ri);
@@ -684,6 +695,7 @@
 		up(&f->sem);
 		jffs2_complete_reservation(c);
 		jffs2_clear_inode(inode);
+		posix_acl_release(acl);
 		return PTR_ERR(fn);
 	}
 	/* No data here. Only a metadata node, which will be
@@ -697,9 +709,10 @@
 	ret = jffs2_init_security(inode, dir_i);
 	if (ret) {
 		jffs2_clear_inode(inode);
+		posix_acl_release(acl);
 		return ret;
 	}
-	ret = jffs2_init_acl(inode, dir_i);
+	ret = jffs2_init_acl(inode, acl);
 	if (ret) {
 		jffs2_clear_inode(inode);
 		return ret;
@@ -770,7 +783,7 @@
 }
 
 static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
-                        struct inode *new_dir_i, struct dentry *new_dentry)
+			 struct inode *new_dir_i, struct dentry *new_dentry)
 {
 	int ret;
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index 66e7c2f..a1db918 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -38,8 +38,8 @@
 #ifdef __ECOS
        ret = jffs2_flash_erase(c, jeb);
        if (!ret) {
-               jffs2_erase_succeeded(c, jeb);
-               return;
+	       jffs2_erase_succeeded(c, jeb);
+	       return;
        }
        bad_offset = jeb->offset;
 #else /* Linux */
@@ -50,12 +50,14 @@
 	instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
 	if (!instr) {
 		printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+		down(&c->erase_free_sem);
 		spin_lock(&c->erase_completion_lock);
 		list_move(&jeb->list, &c->erase_pending_list);
 		c->erasing_size -= c->sector_size;
 		c->dirty_size += c->sector_size;
 		jeb->dirty_size = c->sector_size;
 		spin_unlock(&c->erase_completion_lock);
+		up(&c->erase_free_sem);
 		return;
 	}
 
@@ -82,12 +84,14 @@
 	if (ret == -ENOMEM || ret == -EAGAIN) {
 		/* Erase failed immediately. Refile it on the list */
 		D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
+		down(&c->erase_free_sem);
 		spin_lock(&c->erase_completion_lock);
 		list_move(&jeb->list, &c->erase_pending_list);
 		c->erasing_size -= c->sector_size;
 		c->dirty_size += c->sector_size;
 		jeb->dirty_size = c->sector_size;
 		spin_unlock(&c->erase_completion_lock);
+		up(&c->erase_free_sem);
 		return;
 	}
 
@@ -114,6 +118,7 @@
 			jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
 			list_del(&jeb->list);
 			spin_unlock(&c->erase_completion_lock);
+			up(&c->erase_free_sem);
 			jffs2_mark_erased_block(c, jeb);
 
 			if (!--count) {
@@ -134,6 +139,7 @@
 			jffs2_free_jeb_node_refs(c, jeb);
 			list_add(&jeb->list, &c->erasing_list);
 			spin_unlock(&c->erase_completion_lock);
+			up(&c->erase_free_sem);
 
 			jffs2_erase_block(c, jeb);
 
@@ -142,23 +148,25 @@
 		}
 
 		/* Be nice */
-		cond_resched();
+		yield();
+		down(&c->erase_free_sem);
 		spin_lock(&c->erase_completion_lock);
 	}
 
 	spin_unlock(&c->erase_completion_lock);
+	up(&c->erase_free_sem);
  done:
 	D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
-
-	up(&c->erase_free_sem);
 }
 
 static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
+	down(&c->erase_free_sem);
 	spin_lock(&c->erase_completion_lock);
 	list_move_tail(&jeb->list, &c->erase_complete_list);
 	spin_unlock(&c->erase_completion_lock);
+	up(&c->erase_free_sem);
 	/* Ensure that kupdated calls us again to mark them clean */
 	jffs2_erase_pending_trigger(c);
 }
@@ -172,22 +180,26 @@
 		   failed too many times. */
 		if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
 			/* We'd like to give this block another try. */
+			down(&c->erase_free_sem);
 			spin_lock(&c->erase_completion_lock);
 			list_move(&jeb->list, &c->erase_pending_list);
 			c->erasing_size -= c->sector_size;
 			c->dirty_size += c->sector_size;
 			jeb->dirty_size = c->sector_size;
 			spin_unlock(&c->erase_completion_lock);
+			up(&c->erase_free_sem);
 			return;
 		}
 	}
 
+	down(&c->erase_free_sem);
 	spin_lock(&c->erase_completion_lock);
 	c->erasing_size -= c->sector_size;
 	c->bad_size += c->sector_size;
 	list_move(&jeb->list, &c->bad_list);
 	c->nr_erasing_blocks--;
 	spin_unlock(&c->erase_completion_lock);
+	up(&c->erase_free_sem);
 	wake_up(&c->erase_wait);
 }
 
@@ -317,6 +329,33 @@
 	size_t retlen;
 	int ret = -EIO;
 
+	if (c->mtd->point) {
+		unsigned long *wordebuf;
+
+		ret = c->mtd->point(c->mtd, jeb->offset, c->sector_size, &retlen, (unsigned char **)&ebuf);
+		if (ret) {
+			D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
+			goto do_flash_read;
+		}
+		if (retlen < c->sector_size) {
+			/* Don't muck about if it won't let us point to the whole erase sector */
+			D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
+			c->mtd->unpoint(c->mtd, ebuf, jeb->offset, retlen);
+			goto do_flash_read;
+		}
+		wordebuf = ebuf-sizeof(*wordebuf);
+		retlen /= sizeof(*wordebuf);
+		do {
+		   if (*++wordebuf != ~0)
+			   break;
+		} while(--retlen);
+		c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size);
+		if (retlen)
+			printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n",
+			       *wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf));
+		return 0;
+	}
+ do_flash_read:
 	ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
 	if (!ebuf) {
 		printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset);
@@ -362,7 +401,7 @@
 {
 	size_t retlen;
 	int ret;
-	uint32_t bad_offset;
+	uint32_t uninitialized_var(bad_offset);
 
 	switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
 	case -EAGAIN:	goto refile;
@@ -417,6 +456,7 @@
 		jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);
 	}
 
+	down(&c->erase_free_sem);
 	spin_lock(&c->erase_completion_lock);
 	c->erasing_size -= c->sector_size;
 	c->free_size += jeb->free_size;
@@ -429,23 +469,28 @@
 	c->nr_erasing_blocks--;
 	c->nr_free_blocks++;
 	spin_unlock(&c->erase_completion_lock);
+	up(&c->erase_free_sem);
 	wake_up(&c->erase_wait);
 	return;
 
 filebad:
+	down(&c->erase_free_sem);
 	spin_lock(&c->erase_completion_lock);
 	/* Stick it on a list (any list) so erase_failed can take it
 	   right off again.  Silly, but shouldn't happen often. */
 	list_add(&jeb->list, &c->erasing_list);
 	spin_unlock(&c->erase_completion_lock);
+	up(&c->erase_free_sem);
 	jffs2_erase_failed(c, jeb, bad_offset);
 	return;
 
 refile:
 	/* Stick it back on the list from whence it came and come back later */
 	jffs2_erase_pending_trigger(c);
+	down(&c->erase_free_sem);
 	spin_lock(&c->erase_completion_lock);
 	list_add(&jeb->list, &c->erase_complete_list);
 	spin_unlock(&c->erase_completion_lock);
+	up(&c->erase_free_sem);
 	return;
 }
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 8bc727b..ed85f9a 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -24,7 +24,7 @@
 
 static int jffs2_flash_setup(struct jffs2_sb_info *c);
 
-static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
+int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
 {
 	struct jffs2_full_dnode *old_metadata, *new_metadata;
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
@@ -36,10 +36,8 @@
 	unsigned int ivalid;
 	uint32_t alloclen;
 	int ret;
+
 	D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
-	ret = inode_change_ok(inode, iattr);
-	if (ret)
-		return ret;
 
 	/* Special cases - we don't want more than one data node
 	   for these types on the medium at any time. So setattr
@@ -183,9 +181,14 @@
 {
 	int rc;
 
+	rc = inode_change_ok(dentry->d_inode, iattr);
+	if (rc)
+		return rc;
+
 	rc = jffs2_do_setattr(dentry->d_inode, iattr);
 	if (!rc && (iattr->ia_valid & ATTR_MODE))
 		rc = jffs2_acl_chmod(dentry->d_inode);
+
 	return rc;
 }
 
@@ -399,7 +402,8 @@
 
 /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
    fill in the raw_inode while you're at it. */
-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
+struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri,
+			       struct posix_acl **acl)
 {
 	struct inode *inode;
 	struct super_block *sb = dir_i->i_sb;
@@ -431,7 +435,23 @@
 	} else {
 		ri->gid = cpu_to_je16(current->fsgid);
 	}
-	ri->mode =  cpu_to_jemode(mode);
+
+	/* POSIX ACLs have to be processed now, at least partly.
+	   The umask is only applied if there's no default ACL */
+	if (!S_ISLNK(mode)) {
+		*acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT);
+		if (IS_ERR(*acl)) {
+			make_bad_inode(inode);
+			iput(inode);
+			inode = (void *)*acl;
+			*acl = NULL;
+			return inode;
+		}
+		if (!(*acl))
+			mode &= ~current->fs->umask;
+	} else {
+		*acl = NULL;
+	}
 	ret = jffs2_do_new_inode (c, f, mode, ri);
 	if (ret) {
 		make_bad_inode(inode);
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 2d99e06..32ff037 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -122,6 +122,7 @@
 	struct jffs2_inode_cache *ic;
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_raw_node_ref *raw;
+	uint32_t gcblock_dirty;
 	int ret = 0, inum, nlink;
 	int xattr = 0;
 
@@ -236,6 +237,7 @@
 	}
 
 	raw = jeb->gc_node;
+	gcblock_dirty = jeb->dirty_size;
 
 	while(ref_obsolete(raw)) {
 		D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
@@ -282,7 +284,7 @@
 		} else {
 			ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw);
 		}
-		goto release_sem;
+		goto test_gcnode;
 	}
 #endif
 
@@ -376,7 +378,7 @@
 
 		if (ret != -EBADFD) {
 			spin_unlock(&c->inocache_lock);
-			goto release_sem;
+			goto test_gcnode;
 		}
 
 		/* Fall through if it wanted us to, with inocache_lock held */
@@ -407,6 +409,12 @@
 
 	jffs2_gc_release_inode(c, f);
 
+ test_gcnode:
+	if (jeb->dirty_size == gcblock_dirty && !ref_obsolete(jeb->gc_node)) {
+		/* Eep. This really should never happen. GC is broken */
+		printk(KERN_ERR "Error garbage collecting node at %08x!\n", ref_offset(jeb->gc_node));
+		ret = -ENOSPC;
+	}
  release_sem:
 	up(&c->alloc_sem);
 
@@ -556,7 +564,7 @@
 
 	node = kmalloc(rawlen, GFP_KERNEL);
 	if (!node)
-               return -ENOMEM;
+		return -ENOMEM;
 
 	ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
 	if (!ret && retlen != rawlen)
@@ -598,10 +606,15 @@
 			goto bail;
 		}
 
+		if (strnlen(node->d.name, node->d.nsize) != node->d.nsize) {
+			printk(KERN_WARNING "Name in dirent node at 0x%08x contains zeroes\n", ref_offset(raw));
+			goto bail;
+		}
+
 		if (node->d.nsize) {
 			crc = crc32(0, node->d.name, node->d.nsize);
 			if (je32_to_cpu(node->d.name_crc) != crc) {
-				printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+				printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
 				       ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
 				goto bail;
 			}
@@ -624,7 +637,7 @@
 
 	if (ret || (retlen != rawlen)) {
 		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
-                       rawlen, phys_ofs, ret, retlen);
+		       rawlen, phys_ofs, ret, retlen);
 		if (retlen) {
 			jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL);
 		} else {
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index b13298a..3a2197f 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -69,6 +69,8 @@
 	uint8_t resv_blocks_gctrigger;	/* ... wake up the GC thread */
 	uint8_t resv_blocks_gcbad;	/* ... pick a block from the bad_list to GC */
 	uint8_t resv_blocks_gcmerge;	/* ... merge pages when garbage collecting */
+	/* Number of 'very dirty' blocks before we trigger immediate GC */
+	uint8_t vdirty_blocks_gctrigger;
 
 	uint32_t nospc_dirty_size;
 
@@ -106,6 +108,9 @@
 
 	uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */
 
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+	unsigned char *wbuf_verify; /* read-back buffer for verification */
+#endif
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	unsigned char *wbuf; /* Write-behind buffer for NAND flash */
 	uint32_t wbuf_ofs;
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index bc5509f..ec1aae9 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -127,7 +127,7 @@
 	return ((struct jffs2_inode_cache *)raw);
 }
 
-        /* flash_offset & 3 always has to be zero, because nodes are
+	/* flash_offset & 3 always has to be zero, because nodes are
 	   always aligned at 4 bytes. So we have a couple of extra bits
 	   to play with, which indicate the node's status; see below: */
 #define REF_UNCHECKED	0	/* We haven't yet checked the CRC or built its inode */
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index dbc908a..a0313fa 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -154,7 +154,7 @@
 	while(ret == -EAGAIN) {
 		ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
 		if (ret) {
-		        D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
+			D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
 		}
 	}
 	spin_unlock(&c->erase_completion_lock);
@@ -423,7 +423,12 @@
 	   even after refiling c->nextblock */
 	if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
 	    && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) {
-		printk(KERN_WARNING "argh. node added in wrong place\n");
+		printk(KERN_WARNING "argh. node added in wrong place at 0x%08x(%d)\n", ofs & ~3, ofs & 3);
+		if (c->nextblock)
+			printk(KERN_WARNING "nextblock 0x%08x", c->nextblock->offset);
+		else
+			printk(KERN_WARNING "No nextblock");
+		printk(", expected at %08x\n", jeb->offset + (c->sector_size - jeb->free_size));
 		return ERR_PTR(-EINVAL);
 	}
 #endif
@@ -717,6 +722,8 @@
 {
 	int ret = 0;
 	uint32_t dirty;
+	int nr_very_dirty = 0;
+	struct jffs2_eraseblock *jeb;
 
 	if (c->unchecked_size) {
 		D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
@@ -738,8 +745,18 @@
 			(dirty > c->nospc_dirty_size))
 		ret = 1;
 
-	D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
-		  c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
+	list_for_each_entry(jeb, &c->very_dirty_list, list) {
+		nr_very_dirty++;
+		if (nr_very_dirty == c->vdirty_blocks_gctrigger) {
+			ret = 1;
+			/* In debug mode, actually go through and count them all */
+			D1(continue);
+			break;
+		}
+	}
+
+	D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n",
+		  c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, nr_very_dirty, ret?"yes":"no"));
 
 	return ret;
 }
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index 80daea9..f6743a9 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -173,12 +173,15 @@
 extern const struct inode_operations jffs2_symlink_inode_operations;
 
 /* fs.c */
+struct posix_acl;
+
 int jffs2_setattr (struct dentry *, struct iattr *);
+int jffs2_do_setattr (struct inode *, struct iattr *);
 void jffs2_read_inode (struct inode *);
 void jffs2_clear_inode (struct inode *);
 void jffs2_dirty_inode(struct inode *inode);
 struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
-			       struct jffs2_raw_inode *ri);
+			       struct jffs2_raw_inode *ri, struct posix_acl **acl);
 int jffs2_statfs (struct dentry *, struct kstatfs *);
 void jffs2_write_super (struct super_block *);
 int jffs2_remount_fs (struct super_block *, int *, char *);
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index b5baa35..2eae5d2 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -65,7 +65,7 @@
 		err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
 		if (!err && retlen < tn->csize) {
 			JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
-			c->mtd->unpoint(c->mtd, buffer, ofs, len);
+			c->mtd->unpoint(c->mtd, buffer, ofs, retlen);
 		} else if (err)
 			JFFS2_WARNING("MTD point failed: error code %d.\n", err);
 		else
@@ -211,7 +211,7 @@
  * ordering.
  *
  * Returns 0 if the node was handled (including marking it obsolete)
- *         < 0 an if error occurred
+ *	 < 0 an if error occurred
  */
 static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				struct jffs2_readinode_info *rii,
@@ -862,8 +862,8 @@
 		JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n",
 			    ref_offset(ref));
 		JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n",
-                            je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
-                            je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
+			    je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
+			    je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
 		jffs2_mark_node_obsolete(c, ref);
 		return 0;
 	}
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 6c75cd4..272872d 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -101,7 +101,7 @@
 		if (!ret && pointlen < c->mtd->size) {
 			/* Don't muck about if it won't let us point to the whole flash */
 			D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
-			c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
+			c->mtd->unpoint(c->mtd, flashbuf, 0, pointlen);
 			flashbuf = NULL;
 		}
 		if (ret)
@@ -863,7 +863,7 @@
 			switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
 			case JFFS2_FEATURE_ROCOMPAT:
 				printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
-			        c->flags |= JFFS2_SB_FLAG_RO;
+				c->flags |= JFFS2_SB_FLAG_RO;
 				if (!(jffs2_is_readonly(c)))
 					return -EROFS;
 				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
@@ -1004,6 +1004,7 @@
 {
 	struct jffs2_full_dirent *fd;
 	struct jffs2_inode_cache *ic;
+	uint32_t checkedlen;
 	uint32_t crc;
 	int err;
 
@@ -1024,12 +1025,18 @@
 
 	pseudo_random += je32_to_cpu(rd->version);
 
-	fd = jffs2_alloc_full_dirent(rd->nsize+1);
+	/* Should never happen. Did. (OLPC trac #4184)*/
+	checkedlen = strnlen(rd->name, rd->nsize);
+	if (checkedlen < rd->nsize) {
+		printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n",
+		       ofs, checkedlen);
+	}
+	fd = jffs2_alloc_full_dirent(checkedlen+1);
 	if (!fd) {
 		return -ENOMEM;
 	}
-	memcpy(&fd->name, rd->name, rd->nsize);
-	fd->name[rd->nsize] = 0;
+	memcpy(&fd->name, rd->name, checkedlen);
+	fd->name[checkedlen] = 0;
 
 	crc = crc32(0, fd->name, rd->nsize);
 	if (crc != je32_to_cpu(rd->name_crc)) {
@@ -1055,7 +1062,7 @@
 	fd->next = NULL;
 	fd->version = je32_to_cpu(rd->version);
 	fd->ino = je32_to_cpu(rd->ino);
-	fd->nhash = full_name_hash(fd->name, rd->nsize);
+	fd->nhash = full_name_hash(fd->name, checkedlen);
 	fd->type = rd->type;
 	jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 
diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c
index bc9f6ba..02c39c6 100644
--- a/fs/jffs2/security.c
+++ b/fs/jffs2/security.c
@@ -38,9 +38,9 @@
 	}
 	rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
 
-        kfree(name);
-        kfree(value);
-        return rc;
+	kfree(name);
+	kfree(value);
+	return rc;
 }
 
 /* ---- XATTR Handler for "security.*" ----------------- */
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c
index d828b29..629af01 100644
--- a/fs/jffs2/summary.c
+++ b/fs/jffs2/summary.c
@@ -2,10 +2,10 @@
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
  * Copyright © 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
- *                   Zoltan Sogor <weth@inf.u-szeged.hu>,
- *                   Patrik Kluba <pajko@halom.u-szeged.hu>,
- *                   University of Szeged, Hungary
- *             2006  KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *		     Zoltan Sogor <weth@inf.u-szeged.hu>,
+ *		     Patrik Kluba <pajko@halom.u-szeged.hu>,
+ *		     University of Szeged, Hungary
+ *	       2006  KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
@@ -429,6 +429,7 @@
 
 			case JFFS2_NODETYPE_DIRENT: {
 				struct jffs2_sum_dirent_flash *spd;
+				int checkedlen;
 				spd = sp;
 
 				dbg_summary("Dirent at 0x%08x-0x%08x\n",
@@ -436,12 +437,25 @@
 					    jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen));
 
 
-				fd = jffs2_alloc_full_dirent(spd->nsize+1);
+				/* This should never happen, but https://dev.laptop.org/ticket/4184 */
+				checkedlen = strnlen(spd->name, spd->nsize);
+				if (!checkedlen) {
+					printk(KERN_ERR "Dirent at %08x has zero at start of name. Aborting mount.\n",
+					       jeb->offset + je32_to_cpu(spd->offset));
+					return -EIO;
+				}
+				if (checkedlen < spd->nsize) {
+					printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n",
+					       jeb->offset + je32_to_cpu(spd->offset), checkedlen);
+				}
+
+
+				fd = jffs2_alloc_full_dirent(checkedlen+1);
 				if (!fd)
 					return -ENOMEM;
 
-				memcpy(&fd->name, spd->name, spd->nsize);
-				fd->name[spd->nsize] = 0;
+				memcpy(&fd->name, spd->name, checkedlen);
+				fd->name[checkedlen] = 0;
 
 				ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
 				if (!ic) {
@@ -455,7 +469,7 @@
 				fd->next = NULL;
 				fd->version = je32_to_cpu(spd->version);
 				fd->ino = je32_to_cpu(spd->ino);
-				fd->nhash = full_name_hash(fd->name, spd->nsize);
+				fd->nhash = full_name_hash(fd->name, checkedlen);
 				fd->type = spd->type;
 
 				jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h
index 0c6669e..8bf34f2 100644
--- a/fs/jffs2/summary.h
+++ b/fs/jffs2/summary.h
@@ -2,9 +2,9 @@
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
  * Copyright © 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
- *                   Zoltan Sogor <weth@inf.u-szeged.hu>,
- *                   Patrik Kluba <pajko@halom.u-szeged.hu>,
- *                   University of Szeged, Hungary
+ *		     Zoltan Sogor <weth@inf.u-szeged.hu>,
+ *		     Patrik Kluba <pajko@halom.u-szeged.hu>,
+ *		     University of Szeged, Hungary
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index 91d1d0f..d1d4f27 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -220,6 +220,47 @@
 	return NULL;
 }
 
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
+			      uint32_t ofs)
+{
+	int ret;
+	size_t retlen;
+	char *eccstr;
+
+	ret = c->mtd->read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify);
+	if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
+		printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret);
+		return ret;
+	} else if (retlen != c->wbuf_pagesize) {
+		printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize);
+		return -EIO;
+	}
+	if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize))
+		return 0;
+
+	if (ret == -EUCLEAN)
+		eccstr = "corrected";
+	else if (ret == -EBADMSG)
+		eccstr = "correction failed";
+	else
+		eccstr = "OK or unused";
+
+	printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n",
+	       eccstr, c->wbuf_ofs);
+	print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
+		       c->wbuf, c->wbuf_pagesize, 0);
+
+	printk(KERN_WARNING "Read back:\n");
+	print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
+		       c->wbuf_verify, c->wbuf_pagesize, 0);
+
+	return -EIO;
+}
+#else
+#define jffs2_verify_write(c,b,o) (0)
+#endif
+
 /* Recover from failure to write wbuf. Recover the nodes up to the
  * wbuf, not the one which we were starting to try to write. */
 
@@ -380,7 +421,7 @@
 			ret = c->mtd->write(c->mtd, ofs, towrite, &retlen,
 					    rewrite_buf);
 
-		if (ret || retlen != towrite) {
+		if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) {
 			/* Argh. We tried. Really we did. */
 			printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
 			kfree(buf);
@@ -587,15 +628,16 @@
 
 		ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
 
-	if (ret || retlen != c->wbuf_pagesize) {
-		if (ret)
-			printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
-		else {
-			printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
-				retlen, c->wbuf_pagesize);
-			ret = -EIO;
-		}
-
+	if (ret) {
+		printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret);
+		goto wfail;
+	} else if (retlen != c->wbuf_pagesize) {
+		printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
+		       retlen, c->wbuf_pagesize);
+		ret = -EIO;
+		goto wfail;
+	} else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) {
+	wfail:
 		jffs2_wbuf_recover(c);
 
 		return ret;
@@ -966,8 +1008,8 @@
 
 #define NR_OOB_SCAN_PAGES 4
 
-/* For historical reasons we use only 12 bytes for OOB clean marker */
-#define OOB_CM_SIZE 12
+/* For historical reasons we use only 8 bytes for OOB clean marker */
+#define OOB_CM_SIZE 8
 
 static const struct jffs2_unknown_node oob_cleanmarker =
 {
@@ -1021,8 +1063,8 @@
 /*
  * Check for a valid cleanmarker.
  * Returns: 0 if a valid cleanmarker was found
- *          1 if no cleanmarker was found
- *          negative error code if an error occurred
+ *	    1 if no cleanmarker was found
+ *	    negative error code if an error occurred
  */
 int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
 				 struct jffs2_eraseblock *jeb)
@@ -1138,11 +1180,22 @@
 		return -ENOMEM;
 	}
 
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+	c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+	if (!c->wbuf_verify) {
+		kfree(c->oobbuf);
+		kfree(c->wbuf);
+		return -ENOMEM;
+	}
+#endif
 	return 0;
 }
 
 void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
 {
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+	kfree(c->wbuf_verify);
+#endif
 	kfree(c->wbuf);
 	kfree(c->oobbuf);
 }
diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c
index 664c164..2f56954 100644
--- a/fs/jffs2/write.c
+++ b/fs/jffs2/write.c
@@ -215,6 +215,17 @@
 		BUG();
 	   });
 
+	if (strnlen(name, namelen) != namelen) {
+		/* This should never happen, but seems to have done on at least one
+		   occasion: https://dev.laptop.org/ticket/4184 */
+		printk(KERN_CRIT "Error in jffs2_write_dirent() -- name contains zero bytes!\n");
+		printk(KERN_CRIT "Directory inode #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x\n",
+		       je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
+		       je32_to_cpu(rd->name_crc));
+		WARN_ON(1);
+		return ERR_PTR(-EIO);
+	}
+
 	vecs[0].iov_base = rd;
 	vecs[0].iov_len = sizeof(*rd);
 	vecs[1].iov_base = (unsigned char *)name;
@@ -226,7 +237,7 @@
 
 	fd->version = je32_to_cpu(rd->version);
 	fd->ino = je32_to_cpu(rd->ino);
-	fd->nhash = full_name_hash(name, strlen(name));
+	fd->nhash = full_name_hash(name, namelen);
 	fd->type = rd->type;
 	memcpy(fd->name, name, namelen);
 	fd->name[namelen]=0;
diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h
index 3b0ff29..6e3b5dd 100644
--- a/fs/jffs2/xattr.h
+++ b/fs/jffs2/xattr.h
@@ -75,7 +75,7 @@
 extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
 
 extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
-                                                  uint32_t xid, uint32_t version);
+							 uint32_t xid, uint32_t version);
 
 extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
 extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c
index 40942bc..8bbeab9 100644
--- a/fs/jffs2/xattr_user.c
+++ b/fs/jffs2/xattr_user.c
@@ -17,7 +17,7 @@
 #include "nodelist.h"
 
 static int jffs2_user_getxattr(struct inode *inode, const char *name,
-                               void *buffer, size_t size)
+			       void *buffer, size_t size)
 {
 	if (!strcmp(name, ""))
 		return -EINVAL;
@@ -25,7 +25,7 @@
 }
 
 static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer,
-                               size_t size, int flags)
+			       size_t size, int flags)
 {
 	if (!strcmp(name, ""))
 		return -EINVAL;
diff --git a/include/asm-blackfin/mach-bf548/dma.h b/include/asm-blackfin/mach-bf548/dma.h
index fcc8b4c..14cb10c 100644
--- a/include/asm-blackfin/mach-bf548/dma.h
+++ b/include/asm-blackfin/mach-bf548/dma.h
@@ -55,6 +55,7 @@
 #define CH_SPORT3_RX		20
 #define CH_SPORT3_TX		21
 #define CH_SDH			22
+#define CH_NFC			22
 #define CH_SPI2			23
 
 #define CH_MEM_STREAM0_DEST	24
diff --git a/include/asm-blackfin/nand.h b/include/asm-blackfin/nand.h
new file mode 100644
index 0000000..afbaafa
--- /dev/null
+++ b/include/asm-blackfin/nand.h
@@ -0,0 +1,47 @@
+/* linux/include/asm-blackfin/nand.h
+ *
+ * Copyright (c) 2007 Analog Devices, Inc.
+ *	Bryan Wu <bryan.wu@analog.com>
+ *
+ * BF5XX - NAND flash controller platfrom_device info
+ *
+ * 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.
+ */
+
+/* struct bf5xx_nand_platform
+ *
+ * define a interface between platfrom board specific code and
+ * bf54x NFC driver.
+ *
+ * nr_partitions = number of partitions pointed to be partitoons (or zero)
+ * partitions	 = mtd partition list
+ */
+
+#define NFC_PG_SIZE_256		0
+#define NFC_PG_SIZE_512		1
+#define NFC_PG_SIZE_OFFSET	9
+
+#define NFC_NWIDTH_8		0
+#define NFC_NWIDTH_16		1
+#define NFC_NWIDTH_OFFSET	8
+
+#define NFC_RDDLY_OFFSET	4
+#define NFC_WRDLY_OFFSET	0
+
+#define NFC_STAT_NBUSY		1
+
+struct bf5xx_nand_platform {
+	/* NAND chip information */
+	unsigned short		page_size;
+	unsigned short		data_width;
+
+	/* RD/WR strobe delay timing information, all times in SCLK cycles */
+	unsigned short		rd_dly;
+	unsigned short		wr_dly;
+
+	/* NAND MTD partition information */
+	int                     nr_partitions;
+	struct mtd_partition    *partitions;
+};
diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h
index 840631f..6b563ca 100644
--- a/include/linux/jffs2.h
+++ b/include/linux/jffs2.h
@@ -46,6 +46,7 @@
 #define JFFS2_COMPR_COPY	0x04
 #define JFFS2_COMPR_DYNRUBIN	0x05
 #define JFFS2_COMPR_ZLIB	0x06
+#define JFFS2_COMPR_LZO		0x07
 /* Compatibility flags. */
 #define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
 #define JFFS2_NODE_ACCURATE 0x2000
diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h
index 123948b..e17c534 100644
--- a/include/linux/mtd/cfi.h
+++ b/include/linux/mtd/cfi.h
@@ -57,6 +57,15 @@
 #define cfi_interleave_is_8(cfi) (0)
 #endif
 
+#ifndef cfi_interleave
+#warning No CONFIG_MTD_CFI_Ix selected. No NOR chip support can work.
+static inline int cfi_interleave(void *cfi)
+{
+	BUG();
+	return 0;
+}
+#endif
+
 static inline int cfi_interleave_supported(int i)
 {
 	switch (i) {
diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h
index a293a3b..39e7d2a 100644
--- a/include/linux/mtd/flashchip.h
+++ b/include/linux/mtd/flashchip.h
@@ -40,6 +40,7 @@
 	FL_POINT,
 	FL_XIP_WHILE_ERASING,
 	FL_XIP_WHILE_WRITING,
+	FL_SHUTDOWN,
 	FL_UNKNOWN
 } flstate_t;
 
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h
index 81f3a31..a9fae03 100644
--- a/include/linux/mtd/map.h
+++ b/include/linux/mtd/map.h
@@ -125,7 +125,15 @@
 #endif
 
 #ifndef map_bankwidth
-#error "No bus width supported. What's the point?"
+#warning "No CONFIG_MTD_MAP_BANK_WIDTH_xx selected. No NOR chip support can work"
+static inline int map_bankwidth(void *map)
+{
+	BUG();
+	return 0;
+}
+#define map_bankwidth_is_large(map) (0)
+#define map_words(map) (0)
+#define MAX_MAP_BANKWIDTH 1
 #endif
 
 static inline int map_bankwidth_supported(int w)
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index fd64ccf..783fc98 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -133,6 +133,13 @@
 	int numeraseregions;
 	struct mtd_erase_region_info *eraseregions;
 
+	/*
+	 * Erase is an asynchronous operation.  Device drivers are supposed
+	 * to call instr->callback() whenever the operation completes, even
+	 * if it completes with a failure.
+	 * Callers are supposed to pass a callback function and wait for it
+	 * to be called before writing to the block.
+	 */
 	int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
 
 	/* This stuff for eXecute-In-Place */
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index d2365c8..c42bc7f 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -432,6 +432,7 @@
 #define NAND_MFR_STMICRO	0x20
 #define NAND_MFR_HYNIX		0xad
 #define NAND_MFR_MICRON		0x2c
+#define NAND_MFR_AMD		0x01
 
 /**
  * struct nand_flash_dev - NAND Flash Device ID Structure
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index a56d24a..fd0a260 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -60,6 +60,7 @@
  * @erase_shift:	[INTERN] number of address bits in a block
  * @page_shift:		[INTERN] number of address bits in a page
  * @page_mask:		[INTERN] a page per block mask
+ * @writesize:		[INTERN] a real page size
  * @bufferram_index:	[INTERN] BufferRAM index
  * @bufferram:		[INTERN] BufferRAM info
  * @readw:		[REPLACEABLE] hardware specific function for read short
@@ -100,6 +101,7 @@
 	unsigned int		erase_shift;
 	unsigned int		page_shift;
 	unsigned int		page_mask;
+	unsigned int		writesize;
 
 	unsigned int		bufferram_index;
 	struct onenand_bufferram	bufferram[MAX_BUFFERRAM];
@@ -140,6 +142,8 @@
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)
 #define ONENAND_SET_PREV_BUFFERRAM(this)	(this->bufferram_index ^= 1)
+#define ONENAND_SET_BUFFERRAM0(this)		(this->bufferram_index = 0)
+#define ONENAND_SET_BUFFERRAM1(this)		(this->bufferram_index = 1)
 
 #define ONENAND_GET_SYS_CFG1(this)					\
 	(this->read_word(this->base + ONENAND_REG_SYS_CFG1))
@@ -149,6 +153,13 @@
 #define ONENAND_IS_DDP(this)						\
 	(this->device_id & ONENAND_DEVICE_IS_DDP)
 
+#ifdef CONFIG_MTD_ONENAND_2X_PROGRAM
+#define ONENAND_IS_2PLANE(this)						\
+	(this->options & ONENAND_HAS_2PLANE)
+#else
+#define ONENAND_IS_2PLANE(this)			(0)
+#endif
+
 /* Check byte access in OneNAND */
 #define ONENAND_CHECK_BYTE_ACCESS(addr)		(addr & 0x1)
 
@@ -157,6 +168,7 @@
  */
 #define ONENAND_HAS_CONT_LOCK		(0x0001)
 #define ONENAND_HAS_UNLOCK_ALL		(0x0002)
+#define ONENAND_HAS_2PLANE		(0x0004)
 #define ONENAND_PAGEBUF_ALLOC		(0x1000)
 #define ONENAND_OOBBUF_ALLOC		(0x2000)
 
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
index af94719..c46161f 100644
--- a/include/linux/mtd/onenand_regs.h
+++ b/include/linux/mtd/onenand_regs.h
@@ -74,6 +74,8 @@
 
 #define ONENAND_DEVICE_DENSITY_512Mb	(0x002)
 #define ONENAND_DEVICE_DENSITY_1Gb	(0x003)
+#define ONENAND_DEVICE_DENSITY_2Gb	(0x004)
+#define ONENAND_DEVICE_DENSITY_4Gb	(0x005)
 
 /*
  * Version ID Register F002h (R)
@@ -111,6 +113,8 @@
 #define ONENAND_CMD_READOOB		(0x13)
 #define ONENAND_CMD_PROG		(0x80)
 #define ONENAND_CMD_PROGOOB		(0x1A)
+#define ONENAND_CMD_2X_PROG		(0x7D)
+#define ONENAND_CMD_2X_CACHE_PROG	(0x7F)
 #define ONENAND_CMD_UNLOCK		(0x23)
 #define ONENAND_CMD_LOCK		(0x2A)
 #define ONENAND_CMD_LOCK_TIGHT		(0x2C)