Browse Source

Merge git://git.infradead.org/mtd-2.6

* git://git.infradead.org/mtd-2.6: (84 commits)
  [JFFS2] debug.h: include <linux/sched.h> for current->pid
  [MTD] OneNAND: Handle DDP chip boundary during read-while-load
  [MTD] OneNAND: return ecc error code only when 2-bit ecc occurs
  [MTD] OneNAND: Implement read-while-load
  [MTD] OneNAND: fix onenand_wait bug in read ecc error
  [MTD] OneNAND: release CPU in cycles
  [MTD] OneNAND: add subpage write support
  [MTD] OneNAND: fix onenand_wait bug
  [JFFS2] use the ref_offset macro
  [JFFS2] Reschedule in loops
  [JFFS2] Fix error-path leak in summary scan
  [JFFS2] add cond_resched() when garbage collecting deletion dirent
  [MTD] Nuke IVR leftovers
  [MTD] OneNAND: fix oob handling in recent oob patch
  [MTD] Fix ssfdc blksize typo
  [JFFS2] replace kmalloc+memset with kzalloc
  [MTD] Fix SSFDC build for variable blocksize.
  [MTD] ESB2ROM uses PCI
  [MTD] of_device-based physmap driver
  [MTD] Support combined RedBoot FIS directory and configuration area
  ...
Linus Torvalds 18 years ago
parent
commit
ebcccd14b7
87 changed files with 4404 additions and 868 deletions
  1. 1 2
      arch/ppc/platforms/ev64360.c
  2. 13 2
      drivers/mtd/Kconfig
  3. 8 7
      drivers/mtd/Makefile
  4. 1 2
      drivers/mtd/afs.c
  5. 1 2
      drivers/mtd/chips/amd_flash.c
  6. 3 2
      drivers/mtd/chips/cfi_cmdset_0001.c
  7. 7 4
      drivers/mtd/chips/cfi_cmdset_0002.c
  8. 1 2
      drivers/mtd/chips/cfi_cmdset_0020.c
  9. 2 3
      drivers/mtd/chips/gen_probe.c
  10. 1 2
      drivers/mtd/chips/jedec.c
  11. 16 1
      drivers/mtd/chips/jedec_probe.c
  12. 1 3
      drivers/mtd/chips/map_absent.c
  13. 1 3
      drivers/mtd/chips/map_ram.c
  14. 1 3
      drivers/mtd/chips/map_rom.c
  15. 2 5
      drivers/mtd/chips/sharp.c
  16. 2 3
      drivers/mtd/cmdlinepart.c
  17. 1 2
      drivers/mtd/devices/block2mtd.c
  18. 6 12
      drivers/mtd/devices/ms02-nv.c
  19. 1 1
      drivers/mtd/devices/mtd_dataflash.c
  20. 1 3
      drivers/mtd/devices/phram.c
  21. 2 5
      drivers/mtd/devices/slram.c
  22. 3 4
      drivers/mtd/ftl.c
  23. 5 7
      drivers/mtd/inftlcore.c
  24. 27 44
      drivers/mtd/maps/Kconfig
  25. 3 1
      drivers/mtd/maps/Makefile
  26. 28 6
      drivers/mtd/maps/amd76xrom.c
  27. 1 1
      drivers/mtd/maps/bast-flash.c
  28. 1 2
      drivers/mtd/maps/ceiva.c
  29. 356 0
      drivers/mtd/maps/ck804xrom.c
  30. 0 283
      drivers/mtd/maps/cstm_mips_ixx.c
  31. 450 0
      drivers/mtd/maps/esb2rom.c
  32. 1 3
      drivers/mtd/maps/integrator-flash.c
  33. 3 2
      drivers/mtd/maps/nettel.c
  34. 1 3
      drivers/mtd/maps/omap_nor.c
  35. 1 2
      drivers/mtd/maps/pcmciamtd.c
  36. 2 3
      drivers/mtd/maps/physmap.c
  37. 255 0
      drivers/mtd/maps/physmap_of.c
  38. 1 2
      drivers/mtd/maps/plat-ram.c
  39. 1 3
      drivers/mtd/maps/sa1100-flash.c
  40. 2 6
      drivers/mtd/maps/tqm834x.c
  41. 1 2
      drivers/mtd/maps/tqm8xxl.c
  42. 10 9
      drivers/mtd/mtd_blkdevs.c
  43. 4 6
      drivers/mtd/mtdblock.c
  44. 3 4
      drivers/mtd/mtdblock_ro.c
  45. 11 12
      drivers/mtd/mtdchar.c
  46. 26 17
      drivers/mtd/mtdconcat.c
  47. 78 15
      drivers/mtd/mtdcore.c
  48. 4 4
      drivers/mtd/mtdpart.c
  49. 16 0
      drivers/mtd/nand/Kconfig
  50. 4 1
      drivers/mtd/nand/Makefile
  51. 223 0
      drivers/mtd/nand/at91_nand.c
  52. 770 0
      drivers/mtd/nand/cafe.c
  53. 1381 0
      drivers/mtd/nand/cafe_ecc.c
  54. 2 2
      drivers/mtd/nand/cs553x_nand.c
  55. 1 2
      drivers/mtd/nand/diskonchip.c
  56. 91 42
      drivers/mtd/nand/nand_base.c
  57. 4 7
      drivers/mtd/nand/nand_bbt.c
  58. 2 2
      drivers/mtd/nand/nand_ecc.c
  59. 158 85
      drivers/mtd/nand/nandsim.c
  60. 1 1
      drivers/mtd/nand/ndfc.c
  61. 3 43
      drivers/mtd/nand/rtc_from4.c
  62. 1 1
      drivers/mtd/nand/s3c2410.c
  63. 5 7
      drivers/mtd/nftlcore.c
  64. 2 3
      drivers/mtd/onenand/generic.c
  65. 283 91
      drivers/mtd/onenand/onenand_base.c
  66. 6 8
      drivers/mtd/onenand/onenand_bbt.c
  67. 24 6
      drivers/mtd/redboot.c
  68. 2 1
      drivers/mtd/rfd_ftl.c
  69. 3 4
      drivers/mtd/ssfdc.c
  70. 2 1
      fs/jffs/jffs_fm.c
  71. 2 2
      fs/jffs2/debug.c
  72. 1 0
      fs/jffs2/debug.h
  73. 1 2
      fs/jffs2/fs.c
  74. 2 0
      fs/jffs2/gc.c
  75. 4 6
      fs/jffs2/nodelist.h
  76. 1 2
      fs/jffs2/readinode.c
  77. 4 2
      fs/jffs2/scan.c
  78. 3 3
      fs/jffs2/summary.c
  79. 4 3
      fs/jffs2/super.c
  80. 1 1
      fs/jffs2/symlink.c
  81. 9 12
      fs/jffs2/wbuf.c
  82. 2 3
      fs/jffs2/xattr.c
  83. 2 1
      include/linux/mtd/blktrans.h
  84. 16 8
      include/linux/mtd/mtd.h
  85. 10 5
      include/linux/mtd/nand.h
  86. 7 1
      include/linux/mtd/onenand.h
  87. 1 0
      include/linux/mtd/onenand_regs.h

+ 1 - 2
arch/ppc/platforms/ev64360.c

@@ -358,13 +358,12 @@ ev64360_setup_mtd(void)
 
 
 	ptbl_entries = 3;
 	ptbl_entries = 3;
 
 
-	if ((ptbl = kmalloc(ptbl_entries * sizeof(struct mtd_partition),
+	if ((ptbl = kzalloc(ptbl_entries * sizeof(struct mtd_partition),
 		GFP_KERNEL)) == NULL) {
 		GFP_KERNEL)) == NULL) {
 
 
 		printk(KERN_WARNING "Can't alloc MTD partition table\n");
 		printk(KERN_WARNING "Can't alloc MTD partition table\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
-	memset(ptbl, 0, ptbl_entries * sizeof(struct mtd_partition));
 
 
 	ptbl[0].name = "reserved";
 	ptbl[0].name = "reserved";
 	ptbl[0].offset = 0;
 	ptbl[0].offset = 0;

+ 13 - 2
drivers/mtd/Kconfig

@@ -164,9 +164,15 @@ config MTD_CHAR
 	  memory chips, and also use ioctl() to obtain information about
 	  memory chips, and also use ioctl() to obtain information about
 	  the device, or to erase parts of it.
 	  the device, or to erase parts of it.
 
 
+config MTD_BLKDEVS
+	tristate "Common interface to block layer for MTD 'translation layers'"
+	depends on MTD && BLOCK
+	default n
+
 config MTD_BLOCK
 config MTD_BLOCK
 	tristate "Caching block device access to MTD devices"
 	tristate "Caching block device access to MTD devices"
 	depends on MTD && BLOCK
 	depends on MTD && BLOCK
+	select MTD_BLKDEVS
 	---help---
 	---help---
 	  Although most flash chips have an erase size too large to be useful
 	  Although most flash chips have an erase size too large to be useful
 	  as block devices, it is possible to use MTD devices which are based
 	  as block devices, it is possible to use MTD devices which are based
@@ -189,6 +195,7 @@ config MTD_BLOCK
 config MTD_BLOCK_RO
 config MTD_BLOCK_RO
 	tristate "Readonly block device access to MTD devices"
 	tristate "Readonly block device access to MTD devices"
 	depends on MTD_BLOCK!=y && MTD && BLOCK
 	depends on MTD_BLOCK!=y && MTD && BLOCK
+	select MTD_BLKDEVS
 	help
 	help
 	  This allows you to mount read-only file systems (such as cramfs)
 	  This allows you to mount read-only file systems (such as cramfs)
 	  from an MTD device, without the overhead (and danger) of the caching
 	  from an MTD device, without the overhead (and danger) of the caching
@@ -200,6 +207,7 @@ config MTD_BLOCK_RO
 config FTL
 config FTL
 	tristate "FTL (Flash Translation Layer) support"
 	tristate "FTL (Flash Translation Layer) support"
 	depends on MTD && BLOCK
 	depends on MTD && BLOCK
+	select MTD_BLKDEVS
 	---help---
 	---help---
 	  This provides support for the original Flash Translation Layer which
 	  This provides support for the original Flash Translation Layer which
 	  is part of the PCMCIA specification. It uses a kind of pseudo-
 	  is part of the PCMCIA specification. It uses a kind of pseudo-
@@ -216,6 +224,7 @@ config FTL
 config NFTL
 config NFTL
 	tristate "NFTL (NAND Flash Translation Layer) support"
 	tristate "NFTL (NAND Flash Translation Layer) support"
 	depends on MTD && BLOCK
 	depends on MTD && BLOCK
+	select MTD_BLKDEVS
 	---help---
 	---help---
 	  This provides support for the NAND Flash Translation Layer which is
 	  This provides support for the NAND Flash Translation Layer which is
 	  used on M-Systems' DiskOnChip devices. It uses a kind of pseudo-
 	  used on M-Systems' DiskOnChip devices. It uses a kind of pseudo-
@@ -239,6 +248,7 @@ config NFTL_RW
 config INFTL
 config INFTL
 	tristate "INFTL (Inverse NAND Flash Translation Layer) support"
 	tristate "INFTL (Inverse NAND Flash Translation Layer) support"
 	depends on MTD && BLOCK
 	depends on MTD && BLOCK
+	select MTD_BLKDEVS
 	---help---
 	---help---
 	  This provides support for the Inverse NAND Flash Translation
 	  This provides support for the Inverse NAND Flash Translation
 	  Layer which is used on M-Systems' newer DiskOnChip devices. It
 	  Layer which is used on M-Systems' newer DiskOnChip devices. It
@@ -256,6 +266,7 @@ config INFTL
 config RFD_FTL
 config RFD_FTL
         tristate "Resident Flash Disk (Flash Translation Layer) support"
         tristate "Resident Flash Disk (Flash Translation Layer) support"
 	depends on MTD && BLOCK
 	depends on MTD && BLOCK
+	select MTD_BLKDEVS
 	---help---
 	---help---
 	  This provides support for the flash translation layer known
 	  This provides support for the flash translation layer known
 	  as the Resident Flash Disk (RFD), as used by the Embedded BIOS
 	  as the Resident Flash Disk (RFD), as used by the Embedded BIOS
@@ -265,8 +276,8 @@ config RFD_FTL
 
 
 config SSFDC
 config SSFDC
 	tristate "NAND SSFDC (SmartMedia) read only translation layer"
 	tristate "NAND SSFDC (SmartMedia) read only translation layer"
-	depends on MTD
-	default n
+	depends on MTD && BLOCK
+	select MTD_BLKDEVS
 	help
 	help
 	  This enables read only access to SmartMedia formatted NAND
 	  This enables read only access to SmartMedia formatted NAND
 	  flash. You can mount it with FAT file system.
 	  flash. You can mount it with FAT file system.

+ 8 - 7
drivers/mtd/Makefile

@@ -15,13 +15,14 @@ obj-$(CONFIG_MTD_AFS_PARTS)	+= afs.o
 
 
 # 'Users' - code which presents functionality to userspace.
 # 'Users' - code which presents functionality to userspace.
 obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
 obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
-obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o mtd_blkdevs.o
-obj-$(CONFIG_MTD_BLOCK_RO)	+= mtdblock_ro.o mtd_blkdevs.o
-obj-$(CONFIG_FTL)		+= ftl.o mtd_blkdevs.o
-obj-$(CONFIG_NFTL)		+= nftl.o mtd_blkdevs.o
-obj-$(CONFIG_INFTL)		+= inftl.o mtd_blkdevs.o
-obj-$(CONFIG_RFD_FTL)		+= rfd_ftl.o mtd_blkdevs.o
-obj-$(CONFIG_SSFDC)		+= ssfdc.o mtd_blkdevs.o
+obj-$(CONFIG_MTD_BLKDEVS)	+= mtd_blkdevs.o
+obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o
+obj-$(CONFIG_MTD_BLOCK_RO)	+= mtdblock_ro.o
+obj-$(CONFIG_FTL)		+= ftl.o
+obj-$(CONFIG_NFTL)		+= nftl.o
+obj-$(CONFIG_INFTL)		+= inftl.o
+obj-$(CONFIG_RFD_FTL)		+= rfd_ftl.o
+obj-$(CONFIG_SSFDC)		+= ssfdc.o
 
 
 nftl-objs		:= nftlcore.o nftlmount.o
 nftl-objs		:= nftlcore.o nftlmount.o
 inftl-objs		:= inftlcore.o inftlmount.o
 inftl-objs		:= inftlcore.o inftlmount.o

+ 1 - 2
drivers/mtd/afs.c

@@ -207,11 +207,10 @@ static int parse_afs_partitions(struct mtd_info *mtd,
 	if (!sz)
 	if (!sz)
 		return ret;
 		return ret;
 
 
-	parts = kmalloc(sz, GFP_KERNEL);
+	parts = kzalloc(sz, GFP_KERNEL);
 	if (!parts)
 	if (!parts)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(parts, 0, sz);
 	str = (char *)(parts + idx);
 	str = (char *)(parts + idx);
 
 
 	/*
 	/*

+ 1 - 2
drivers/mtd/chips/amd_flash.c

@@ -643,13 +643,12 @@ static struct mtd_info *amd_flash_probe(struct map_info *map)
 	int reg_idx;
 	int reg_idx;
 	int offset;
 	int offset;
 
 
-	mtd = (struct mtd_info*)kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	if (!mtd) {
 	if (!mtd) {
 		printk(KERN_WARNING
 		printk(KERN_WARNING
 		       "%s: kmalloc failed for info structure\n", map->name);
 		       "%s: kmalloc failed for info structure\n", map->name);
 		return NULL;
 		return NULL;
 	}
 	}
-	memset(mtd, 0, sizeof(*mtd));
 	mtd->priv = map;
 	mtd->priv = map;
 
 
 	memset(&temp, 0, sizeof(temp));
 	memset(&temp, 0, sizeof(temp));

+ 3 - 2
drivers/mtd/chips/cfi_cmdset_0001.c

@@ -337,12 +337,11 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
 	int i;
 	int i;
 
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	if (!mtd) {
 	if (!mtd) {
 		printk(KERN_ERR "Failed to allocate memory for MTD device\n");
 		printk(KERN_ERR "Failed to allocate memory for MTD device\n");
 		return NULL;
 		return NULL;
 	}
 	}
-	memset(mtd, 0, sizeof(*mtd));
 	mtd->priv = map;
 	mtd->priv = map;
 	mtd->type = MTD_NORFLASH;
 	mtd->type = MTD_NORFLASH;
 
 
@@ -2224,6 +2223,8 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
 		case FL_CFI_QUERY:
 		case FL_CFI_QUERY:
 		case FL_JEDEC_QUERY:
 		case FL_JEDEC_QUERY:
 			if (chip->oldstate == FL_READY) {
 			if (chip->oldstate == FL_READY) {
+				/* place the chip in a known state before suspend */
+				map_write(map, CMD(0xFF), cfi->chips[i].start);
 				chip->oldstate = chip->state;
 				chip->oldstate = chip->state;
 				chip->state = FL_PM_SUSPENDED;
 				chip->state = FL_PM_SUSPENDED;
 				/* No need to wake_up() on this state change -
 				/* No need to wake_up() on this state change -

+ 7 - 4
drivers/mtd/chips/cfi_cmdset_0002.c

@@ -48,6 +48,7 @@
 #define MANUFACTURER_ATMEL	0x001F
 #define MANUFACTURER_ATMEL	0x001F
 #define MANUFACTURER_SST	0x00BF
 #define MANUFACTURER_SST	0x00BF
 #define SST49LF004B	        0x0060
 #define SST49LF004B	        0x0060
+#define SST49LF040B	        0x0050
 #define SST49LF008A		0x005a
 #define SST49LF008A		0x005a
 #define AT49BV6416		0x00d6
 #define AT49BV6416		0x00d6
 
 
@@ -233,6 +234,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
 };
 };
 static struct cfi_fixup jedec_fixup_table[] = {
 static struct cfi_fixup jedec_fixup_table[] = {
 	{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
 	{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
+	{ MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, },
 	{ MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
 	{ MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
 	{ 0, 0, NULL, NULL }
 	{ 0, 0, NULL, NULL }
 };
 };
@@ -255,12 +257,11 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
 	int i;
 	int i;
 
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	if (!mtd) {
 	if (!mtd) {
 		printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
 		printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
 		return NULL;
 		return NULL;
 	}
 	}
-	memset(mtd, 0, sizeof(*mtd));
 	mtd->priv = map;
 	mtd->priv = map;
 	mtd->type = MTD_NORFLASH;
 	mtd->type = MTD_NORFLASH;
 
 
@@ -519,10 +520,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
 		if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
 		if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
 			goto sleep;
 			goto sleep;
 
 
-		if (!(mode == FL_READY || mode == FL_POINT
+		if (!(   mode == FL_READY
+		      || mode == FL_POINT
 		      || !cfip
 		      || !cfip
 		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
 		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
-		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1))))
+		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)
+		    )))
 			goto sleep;
 			goto sleep;
 
 
 		/* We could check to see if we're trying to access the sector
 		/* We could check to see if we're trying to access the sector

+ 1 - 2
drivers/mtd/chips/cfi_cmdset_0020.c

@@ -172,7 +172,7 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
 	int i,j;
 	int i,j;
 	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
 	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
 
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
 	//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
 
 
 	if (!mtd) {
 	if (!mtd) {
@@ -181,7 +181,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	memset(mtd, 0, sizeof(*mtd));
 	mtd->priv = map;
 	mtd->priv = map;
 	mtd->type = MTD_NORFLASH;
 	mtd->type = MTD_NORFLASH;
 	mtd->size = devsize * cfi->numchips;
 	mtd->size = devsize * cfi->numchips;

+ 2 - 3
drivers/mtd/chips/gen_probe.c

@@ -40,7 +40,7 @@ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
 	if (mtd) {
 	if (mtd) {
 		if (mtd->size > map->size) {
 		if (mtd->size > map->size) {
 			printk(KERN_WARNING "Reducing visibility of %ldKiB chip to %ldKiB\n",
 			printk(KERN_WARNING "Reducing visibility of %ldKiB chip to %ldKiB\n",
-			       (unsigned long)mtd->size >> 10, 
+			       (unsigned long)mtd->size >> 10,
 			       (unsigned long)map->size >> 10);
 			       (unsigned long)map->size >> 10);
 			mtd->size = map->size;
 			mtd->size = map->size;
 		}
 		}
@@ -113,13 +113,12 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
 	}
 	}
 
 
 	mapsize = (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG;
 	mapsize = (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG;
-	chip_map = kmalloc(mapsize, GFP_KERNEL);
+	chip_map = kzalloc(mapsize, GFP_KERNEL);
 	if (!chip_map) {
 	if (!chip_map) {
 		printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
 		printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
 		kfree(cfi.cfiq);
 		kfree(cfi.cfiq);
 		return NULL;
 		return NULL;
 	}
 	}
-	memset (chip_map, 0, mapsize);
 
 
 	set_bit(0, chip_map); /* Mark first chip valid */
 	set_bit(0, chip_map); /* Mark first chip valid */
 
 

+ 1 - 2
drivers/mtd/chips/jedec.c

@@ -116,11 +116,10 @@ static struct mtd_info *jedec_probe(struct map_info *map)
    char Part[200];
    char Part[200];
    memset(&priv,0,sizeof(priv));
    memset(&priv,0,sizeof(priv));
 
 
-   MTD = kmalloc(sizeof(struct mtd_info) + sizeof(struct jedec_private), GFP_KERNEL);
+   MTD = kzalloc(sizeof(struct mtd_info) + sizeof(struct jedec_private), GFP_KERNEL);
    if (!MTD)
    if (!MTD)
 	   return NULL;
 	   return NULL;
 
 
-   memset(MTD, 0, sizeof(struct mtd_info) + sizeof(struct jedec_private));
    priv = (struct jedec_private *)&MTD[1];
    priv = (struct jedec_private *)&MTD[1];
 
 
    my_bank_size = map->size;
    my_bank_size = map->size;

+ 16 - 1
drivers/mtd/chips/jedec_probe.c

@@ -154,6 +154,7 @@
 #define SST39SF010A	0x00B5
 #define SST39SF010A	0x00B5
 #define SST39SF020A	0x00B6
 #define SST39SF020A	0x00B6
 #define SST49LF004B	0x0060
 #define SST49LF004B	0x0060
+#define SST49LF040B	0x0050
 #define SST49LF008A	0x005a
 #define SST49LF008A	0x005a
 #define SST49LF030A	0x001C
 #define SST49LF030A	0x001C
 #define SST49LF040A	0x0051
 #define SST49LF040A	0x0051
@@ -1400,6 +1401,20 @@ static const struct amd_flash_info jedec_table[] = {
 			ERASEINFO(0x01000,64),
 			ERASEINFO(0x01000,64),
 		}
 		}
 	}, {
 	}, {
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id         = SST49LF040B,
+		.name           = "SST 49LF040B",
+		.uaddr          = {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize        = SIZE_512KiB,
+		.CmdSet         = P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions        = {
+			ERASEINFO(0x01000,128),
+		}
+	}, {
+
 		.mfr_id		= MANUFACTURER_SST,
 		.mfr_id		= MANUFACTURER_SST,
 		.dev_id		= SST49LF004B,
 		.dev_id		= SST49LF004B,
 		.name		= "SST 49LF004B",
 		.name		= "SST 49LF004B",
@@ -1874,7 +1889,7 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
 
 
 
 
 /*
 /*
- * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing
+ * There is a BIG problem properly ID'ing the JEDEC device and guaranteeing
  * the mapped address, unlock addresses, and proper chip ID.  This function
  * the mapped address, unlock addresses, and proper chip ID.  This function
  * attempts to minimize errors.  It is doubtfull that this probe will ever
  * attempts to minimize errors.  It is doubtfull that this probe will ever
  * be perfect - consequently there should be some module parameters that
  * be perfect - consequently there should be some module parameters that

+ 1 - 3
drivers/mtd/chips/map_absent.c

@@ -47,13 +47,11 @@ static struct mtd_info *map_absent_probe(struct map_info *map)
 {
 {
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
 
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	if (!mtd) {
 	if (!mtd) {
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	memset(mtd, 0, sizeof(*mtd));
-
 	map->fldrv 	= &map_absent_chipdrv;
 	map->fldrv 	= &map_absent_chipdrv;
 	mtd->priv 	= map;
 	mtd->priv 	= map;
 	mtd->name 	= map->name;
 	mtd->name 	= map->name;

+ 1 - 3
drivers/mtd/chips/map_ram.c

@@ -55,12 +55,10 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
 #endif
 #endif
 	/* OK. It seems to be RAM. */
 	/* OK. It seems to be RAM. */
 
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	if (!mtd)
 	if (!mtd)
 		return NULL;
 		return NULL;
 
 
-	memset(mtd, 0, sizeof(*mtd));
-
 	map->fldrv = &mapram_chipdrv;
 	map->fldrv = &mapram_chipdrv;
 	mtd->priv = map;
 	mtd->priv = map;
 	mtd->name = map->name;
 	mtd->name = map->name;

+ 1 - 3
drivers/mtd/chips/map_rom.c

@@ -31,12 +31,10 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
 {
 {
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
 
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	if (!mtd)
 	if (!mtd)
 		return NULL;
 		return NULL;
 
 
-	memset(mtd, 0, sizeof(*mtd));
-
 	map->fldrv = &maprom_chipdrv;
 	map->fldrv = &maprom_chipdrv;
 	mtd->priv = map;
 	mtd->priv = map;
 	mtd->name = map->name;
 	mtd->name = map->name;

+ 2 - 5
drivers/mtd/chips/sharp.c

@@ -112,18 +112,16 @@ static struct mtd_info *sharp_probe(struct map_info *map)
 	struct sharp_info *sharp = NULL;
 	struct sharp_info *sharp = NULL;
 	int width;
 	int width;
 
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	if(!mtd)
 	if(!mtd)
 		return NULL;
 		return NULL;
 
 
-	sharp = kmalloc(sizeof(*sharp), GFP_KERNEL);
+	sharp = kzalloc(sizeof(*sharp), GFP_KERNEL);
 	if(!sharp) {
 	if(!sharp) {
 		kfree(mtd);
 		kfree(mtd);
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	memset(mtd, 0, sizeof(*mtd));
-
 	width = sharp_probe_map(map,mtd);
 	width = sharp_probe_map(map,mtd);
 	if(!width){
 	if(!width){
 		kfree(mtd);
 		kfree(mtd);
@@ -143,7 +141,6 @@ static struct mtd_info *sharp_probe(struct map_info *map)
 	mtd->writesize = 1;
 	mtd->writesize = 1;
 	mtd->name = map->name;
 	mtd->name = map->name;
 
 
-	memset(sharp, 0, sizeof(*sharp));
 	sharp->chipshift = 23;
 	sharp->chipshift = 23;
 	sharp->numchips = 1;
 	sharp->numchips = 1;
 	sharp->chips[0].start = 0;
 	sharp->chips[0].start = 0;

+ 2 - 3
drivers/mtd/cmdlinepart.c

@@ -163,13 +163,12 @@ static struct mtd_partition * newpart(char *s,
 		*num_parts = this_part + 1;
 		*num_parts = this_part + 1;
 		alloc_size = *num_parts * sizeof(struct mtd_partition) +
 		alloc_size = *num_parts * sizeof(struct mtd_partition) +
 			     extra_mem_size;
 			     extra_mem_size;
-		parts = kmalloc(alloc_size, GFP_KERNEL);
+		parts = kzalloc(alloc_size, GFP_KERNEL);
 		if (!parts)
 		if (!parts)
 		{
 		{
 			printk(KERN_ERR ERRP "out of memory\n");
 			printk(KERN_ERR ERRP "out of memory\n");
 			return NULL;
 			return NULL;
 		}
 		}
-		memset(parts, 0, alloc_size);
 		extra_mem = (unsigned char *)(parts + *num_parts);
 		extra_mem = (unsigned char *)(parts + *num_parts);
 	}
 	}
 	/* enter this partition (offset will be calculated later if it is zero at this point) */
 	/* enter this partition (offset will be calculated later if it is zero at this point) */
@@ -346,7 +345,7 @@ static int parse_cmdline_partitions(struct mtd_info *master,
  *
  *
  * This function needs to be visible for bootloaders.
  * This function needs to be visible for bootloaders.
  */
  */
-int mtdpart_setup(char *s)
+static int mtdpart_setup(char *s)
 {
 {
 	cmdline = s;
 	cmdline = s;
 	return 1;
 	return 1;

+ 1 - 2
drivers/mtd/devices/block2mtd.c

@@ -295,10 +295,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
 	if (!devname)
 	if (!devname)
 		return NULL;
 		return NULL;
 
 
-	dev = kmalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
+	dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
 	if (!dev)
 	if (!dev)
 		return NULL;
 		return NULL;
-	memset(dev, 0, sizeof(*dev));
 
 
 	/* Get a handle on the device */
 	/* Get a handle on the device */
 	bdev = open_bdev_excl(devname, O_RDWR, NULL);
 	bdev = open_bdev_excl(devname, O_RDWR, NULL);

+ 6 - 12
drivers/mtd/devices/ms02-nv.c

@@ -131,11 +131,10 @@ static int __init ms02nv_init_one(ulong addr)
 	int ret = -ENODEV;
 	int ret = -ENODEV;
 
 
 	/* The module decodes 8MiB of address space. */
 	/* The module decodes 8MiB of address space. */
-	mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL);
+	mod_res = kzalloc(sizeof(*mod_res), GFP_KERNEL);
 	if (!mod_res)
 	if (!mod_res)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(mod_res, 0, sizeof(*mod_res));
 	mod_res->name = ms02nv_name;
 	mod_res->name = ms02nv_name;
 	mod_res->start = addr;
 	mod_res->start = addr;
 	mod_res->end = addr + MS02NV_SLOT_SIZE - 1;
 	mod_res->end = addr + MS02NV_SLOT_SIZE - 1;
@@ -153,24 +152,21 @@ static int __init ms02nv_init_one(ulong addr)
 	}
 	}
 
 
 	ret = -ENOMEM;
 	ret = -ENOMEM;
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 	if (!mtd)
 	if (!mtd)
 		goto err_out_mod_res_rel;
 		goto err_out_mod_res_rel;
-	memset(mtd, 0, sizeof(*mtd));
-	mp = kmalloc(sizeof(*mp), GFP_KERNEL);
+	mp = kzalloc(sizeof(*mp), GFP_KERNEL);
 	if (!mp)
 	if (!mp)
 		goto err_out_mtd;
 		goto err_out_mtd;
-	memset(mp, 0, sizeof(*mp));
 
 
 	mtd->priv = mp;
 	mtd->priv = mp;
 	mp->resource.module = mod_res;
 	mp->resource.module = mod_res;
 
 
 	/* Firmware's diagnostic NVRAM area. */
 	/* Firmware's diagnostic NVRAM area. */
-	diag_res = kmalloc(sizeof(*diag_res), GFP_KERNEL);
+	diag_res = kzalloc(sizeof(*diag_res), GFP_KERNEL);
 	if (!diag_res)
 	if (!diag_res)
 		goto err_out_mp;
 		goto err_out_mp;
 
 
-	memset(diag_res, 0, sizeof(*diag_res));
 	diag_res->name = ms02nv_res_diag_ram;
 	diag_res->name = ms02nv_res_diag_ram;
 	diag_res->start = addr;
 	diag_res->start = addr;
 	diag_res->end = addr + MS02NV_RAM - 1;
 	diag_res->end = addr + MS02NV_RAM - 1;
@@ -180,11 +176,10 @@ static int __init ms02nv_init_one(ulong addr)
 	mp->resource.diag_ram = diag_res;
 	mp->resource.diag_ram = diag_res;
 
 
 	/* User-available general-purpose NVRAM area. */
 	/* User-available general-purpose NVRAM area. */
-	user_res = kmalloc(sizeof(*user_res), GFP_KERNEL);
+	user_res = kzalloc(sizeof(*user_res), GFP_KERNEL);
 	if (!user_res)
 	if (!user_res)
 		goto err_out_diag_res;
 		goto err_out_diag_res;
 
 
-	memset(user_res, 0, sizeof(*user_res));
 	user_res->name = ms02nv_res_user_ram;
 	user_res->name = ms02nv_res_user_ram;
 	user_res->start = addr + MS02NV_RAM;
 	user_res->start = addr + MS02NV_RAM;
 	user_res->end = addr + size - 1;
 	user_res->end = addr + size - 1;
@@ -194,11 +189,10 @@ static int __init ms02nv_init_one(ulong addr)
 	mp->resource.user_ram = user_res;
 	mp->resource.user_ram = user_res;
 
 
 	/* Control and status register. */
 	/* Control and status register. */
-	csr_res = kmalloc(sizeof(*csr_res), GFP_KERNEL);
+	csr_res = kzalloc(sizeof(*csr_res), GFP_KERNEL);
 	if (!csr_res)
 	if (!csr_res)
 		goto err_out_user_res;
 		goto err_out_user_res;
 
 
-	memset(csr_res, 0, sizeof(*csr_res));
 	csr_res->name = ms02nv_res_csr;
 	csr_res->name = ms02nv_res_csr;
 	csr_res->start = addr + MS02NV_CSR;
 	csr_res->start = addr + MS02NV_CSR;
 	csr_res->end = addr + MS02NV_CSR + 3;
 	csr_res->end = addr + MS02NV_CSR + 3;

+ 1 - 1
drivers/mtd/devices/mtd_dataflash.c

@@ -480,7 +480,7 @@ add_dataflash(struct spi_device *spi, char *name,
 	device->writesize = pagesize;
 	device->writesize = pagesize;
 	device->owner = THIS_MODULE;
 	device->owner = THIS_MODULE;
 	device->type = MTD_DATAFLASH;
 	device->type = MTD_DATAFLASH;
-	device->flags = MTD_CAP_NORFLASH;
+	device->flags = MTD_WRITEABLE;
 	device->erase = dataflash_erase;
 	device->erase = dataflash_erase;
 	device->read = dataflash_read;
 	device->read = dataflash_read;
 	device->write = dataflash_write;
 	device->write = dataflash_write;

+ 1 - 3
drivers/mtd/devices/phram.c

@@ -126,12 +126,10 @@ static int register_device(char *name, unsigned long start, unsigned long len)
 	struct phram_mtd_list *new;
 	struct phram_mtd_list *new;
 	int ret = -ENOMEM;
 	int ret = -ENOMEM;
 
 
-	new = kmalloc(sizeof(*new), GFP_KERNEL);
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
 	if (!new)
 	if (!new)
 		goto out0;
 		goto out0;
 
 
-	memset(new, 0, sizeof(*new));
-
 	ret = -EIO;
 	ret = -EIO;
 	new->mtd.priv = ioremap(start, len);
 	new->mtd.priv = ioremap(start, len);
 	if (!new->mtd.priv) {
 	if (!new->mtd.priv) {

+ 2 - 5
drivers/mtd/devices/slram.c

@@ -168,19 +168,16 @@ static int register_device(char *name, unsigned long start, unsigned long length
 		E("slram: Cannot allocate new MTD device.\n");
 		E("slram: Cannot allocate new MTD device.\n");
 		return(-ENOMEM);
 		return(-ENOMEM);
 	}
 	}
-	(*curmtd)->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
+	(*curmtd)->mtdinfo = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
 	(*curmtd)->next = NULL;
 	(*curmtd)->next = NULL;
 
 
 	if ((*curmtd)->mtdinfo)	{
 	if ((*curmtd)->mtdinfo)	{
-		memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info));
 		(*curmtd)->mtdinfo->priv =
 		(*curmtd)->mtdinfo->priv =
-			kmalloc(sizeof(slram_priv_t), GFP_KERNEL);
+			kzalloc(sizeof(slram_priv_t), GFP_KERNEL);
 
 
 		if (!(*curmtd)->mtdinfo->priv) {
 		if (!(*curmtd)->mtdinfo->priv) {
 			kfree((*curmtd)->mtdinfo);
 			kfree((*curmtd)->mtdinfo);
 			(*curmtd)->mtdinfo = NULL;
 			(*curmtd)->mtdinfo = NULL;
-		} else {
-			memset((*curmtd)->mtdinfo->priv,0,sizeof(slram_priv_t));
 		}
 		}
 	}
 	}
 
 

+ 3 - 4
drivers/mtd/ftl.c

@@ -1033,7 +1033,7 @@ static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
 {
 	partition_t *partition;
 	partition_t *partition;
 
 
-	partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
+	partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
 
 
 	if (!partition) {
 	if (!partition) {
 		printk(KERN_WARNING "No memory to scan for FTL on %s\n",
 		printk(KERN_WARNING "No memory to scan for FTL on %s\n",
@@ -1041,8 +1041,6 @@ static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 		return;
 		return;
 	}
 	}
 
 
-	memset(partition, 0, sizeof(partition_t));
-
 	partition->mbd.mtd = mtd;
 	partition->mbd.mtd = mtd;
 
 
 	if ((scan_header(partition) == 0) &&
 	if ((scan_header(partition) == 0) &&
@@ -1054,7 +1052,7 @@ static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 		       le32_to_cpu(partition->header.FormattedSize) >> 10);
 		       le32_to_cpu(partition->header.FormattedSize) >> 10);
 #endif
 #endif
 		partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
 		partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
-		partition->mbd.blksize = SECTOR_SIZE;
+
 		partition->mbd.tr = tr;
 		partition->mbd.tr = tr;
 		partition->mbd.devnum = -1;
 		partition->mbd.devnum = -1;
 		if (!add_mtd_blktrans_dev((void *)partition))
 		if (!add_mtd_blktrans_dev((void *)partition))
@@ -1076,6 +1074,7 @@ struct mtd_blktrans_ops ftl_tr = {
 	.name		= "ftl",
 	.name		= "ftl",
 	.major		= FTL_MAJOR,
 	.major		= FTL_MAJOR,
 	.part_bits	= PART_BITS,
 	.part_bits	= PART_BITS,
+	.blksize 	= SECTOR_SIZE,
 	.readsect	= ftl_readsect,
 	.readsect	= ftl_readsect,
 	.writesect	= ftl_writesect,
 	.writesect	= ftl_writesect,
 	.getgeo		= ftl_getgeo,
 	.getgeo		= ftl_getgeo,

+ 5 - 7
drivers/mtd/inftlcore.c

@@ -67,17 +67,16 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
 	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
 
 
-	inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
+	inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);
 
 
 	if (!inftl) {
 	if (!inftl) {
 		printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
 		printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
 		return;
 		return;
 	}
 	}
-	memset(inftl, 0, sizeof(*inftl));
 
 
 	inftl->mbd.mtd = mtd;
 	inftl->mbd.mtd = mtd;
 	inftl->mbd.devnum = -1;
 	inftl->mbd.devnum = -1;
-	inftl->mbd.blksize = 512;
+
 	inftl->mbd.tr = tr;
 	inftl->mbd.tr = tr;
 
 
 	if (INFTL_mount(inftl) < 0) {
 	if (INFTL_mount(inftl) < 0) {
@@ -163,10 +162,9 @@ int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 	ops.ooblen = len;
 	ops.ooblen = len;
 	ops.oobbuf = buf;
 	ops.oobbuf = buf;
 	ops.datbuf = NULL;
 	ops.datbuf = NULL;
-	ops.len = len;
 
 
 	res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
 	res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
-	*retlen = ops.retlen;
+	*retlen = ops.oobretlen;
 	return res;
 	return res;
 }
 }
 
 
@@ -184,10 +182,9 @@ int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 	ops.ooblen = len;
 	ops.ooblen = len;
 	ops.oobbuf = buf;
 	ops.oobbuf = buf;
 	ops.datbuf = NULL;
 	ops.datbuf = NULL;
-	ops.len = len;
 
 
 	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
 	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
-	*retlen = ops.retlen;
+	*retlen = ops.oobretlen;
 	return res;
 	return res;
 }
 }
 
 
@@ -945,6 +942,7 @@ static struct mtd_blktrans_ops inftl_tr = {
 	.name		= "inftl",
 	.name		= "inftl",
 	.major		= INFTL_MAJOR,
 	.major		= INFTL_MAJOR,
 	.part_bits	= INFTL_PARTN_BITS,
 	.part_bits	= INFTL_PARTN_BITS,
+	.blksize 	= 512,
 	.getgeo		= inftl_getgeo,
 	.getgeo		= inftl_getgeo,
 	.readsect	= inftl_readblock,
 	.readsect	= inftl_readblock,
 	.writesect	= inftl_writeblock,
 	.writesect	= inftl_writeblock,

+ 27 - 44
drivers/mtd/maps/Kconfig

@@ -60,6 +60,15 @@ config MTD_PHYSMAP_BANKWIDTH
 	  Ignore this option if you use run-time physmap configuration
 	  Ignore this option if you use run-time physmap configuration
 	  (i.e., run-time calling physmap_configure()).
 	  (i.e., run-time calling physmap_configure()).
 
 
+config MTD_PHYSMAP_OF
+	tristate "Flash device in physical memory map based on OF descirption"
+	depends on PPC_OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM)
+	help
+	  This provides a 'mapping' driver which allows the NOR Flash and
+	  ROM driver code to communicate with chips which are mapped
+	  physically into the CPU's memory. The mapping description here is
+	  taken from OF device tree.
+
 config MTD_SUN_UFLASH
 config MTD_SUN_UFLASH
 	tristate "Sun Microsystems userflash support"
 	tristate "Sun Microsystems userflash support"
 	depends on SPARC && MTD_CFI
 	depends on SPARC && MTD_CFI
@@ -184,6 +193,24 @@ config MTD_ICHXROM
 
 
 	  BE VERY CAREFUL.
 	  BE VERY CAREFUL.
 
 
+config MTD_ESB2ROM
+        tristate "BIOS flash chip on Intel ESB Controller Hub 2"
+        depends on X86 && MTD_JEDECPROBE && PCI
+        help
+          Support for treating the BIOS flash chip on ESB2 motherboards
+          as an MTD device - with this you can reprogram your BIOS.
+
+          BE VERY CAREFUL.
+
+config MTD_CK804XROM
+	tristate "BIOS flash chip on Nvidia CK804"
+	depends on X86 && MTD_JEDECPROBE
+	help
+	  Support for treating the BIOS flash chip on nvidia motherboards
+	  as an MTD device - with this you can reprogram your BIOS.
+
+	  BE VERY CAREFUL.
+
 config MTD_SCB2_FLASH
 config MTD_SCB2_FLASH
 	tristate "BIOS flash chip on Intel SCB2 boards"
 	tristate "BIOS flash chip on Intel SCB2 boards"
 	depends on X86 && MTD_JEDECPROBE
 	depends on X86 && MTD_JEDECPROBE
@@ -355,50 +382,6 @@ config MTD_TQM834x
 	  TQ Components TQM834x boards. If you have one of these boards
 	  TQ Components TQM834x boards. If you have one of these boards
 	  and would like to use the flash chips on it, say 'Y'.
 	  and would like to use the flash chips on it, say 'Y'.
 
 
-config MTD_CSTM_MIPS_IXX
-	tristate "Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board"
-	depends on MIPS && MTD_CFI && MTD_JEDECPROBE && MTD_PARTITIONS
-	help
-	  This provides a mapping driver for the Integrated Technology
-	  Express, Inc (ITE) QED-4N-S01B eval board and the Globespan IVR
-	  Reference Board. It provides the necessary addressing, length,
-	  buswidth, vpp code and addition setup of the flash device for
-	  these boards. In addition, this mapping driver can be used for
-	  other boards via setting of the CONFIG_MTD_CSTM_MIPS_IXX_START/
-	  LEN/BUSWIDTH parameters. This mapping will provide one mtd device
-	  using one partition. The start address can be offset from the
-	  beginning of flash and the len can be less than the total flash
-	  device size to allow a window into the flash. Both CFI and JEDEC
-	  probes are called.
-
-config MTD_CSTM_MIPS_IXX_START
-	hex "Physical start address of flash mapping"
-	depends on MTD_CSTM_MIPS_IXX
-	default "0x8000000"
-	help
-	  This is the physical memory location that the MTD driver will
-	  use for the flash chips on your particular target board.
-	  Refer to the memory map which should hopefully be in the
-	  documentation for your board.
-
-config MTD_CSTM_MIPS_IXX_LEN
-	hex "Physical length of flash mapping"
-	depends on MTD_CSTM_MIPS_IXX
-	default "0x4000000"
-	help
-	  This is the total length that the MTD driver will use for the
-	  flash chips on your particular board.  Refer to the memory
-	  map which should hopefully be in the documentation for your
-	  board.
-
-config MTD_CSTM_MIPS_IXX_BUSWIDTH
-	int "Bus width in octets"
-	depends on MTD_CSTM_MIPS_IXX
-	default "2"
-	help
-	  This is the total bus width of the mapping of the flash chips
-	  on your particular board.
-
 config MTD_OCELOT
 config MTD_OCELOT
 	tristate "Momenco Ocelot boot flash device"
 	tristate "Momenco Ocelot boot flash device"
 	depends on MIPS && MOMENCO_OCELOT
 	depends on MIPS && MOMENCO_OCELOT

+ 3 - 1
drivers/mtd/maps/Makefile

@@ -12,12 +12,13 @@ obj-$(CONFIG_MTD_CDB89712)	+= cdb89712.o
 obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o
 obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o
 obj-$(CONFIG_MTD_BAST)		+= bast-flash.o
 obj-$(CONFIG_MTD_BAST)		+= bast-flash.o
 obj-$(CONFIG_MTD_CFI_FLAGADM)	+= cfi_flagadm.o
 obj-$(CONFIG_MTD_CFI_FLAGADM)	+= cfi_flagadm.o
-obj-$(CONFIG_MTD_CSTM_MIPS_IXX)	+= cstm_mips_ixx.o
 obj-$(CONFIG_MTD_DC21285)	+= dc21285.o
 obj-$(CONFIG_MTD_DC21285)	+= dc21285.o
 obj-$(CONFIG_MTD_DILNETPC)	+= dilnetpc.o
 obj-$(CONFIG_MTD_DILNETPC)	+= dilnetpc.o
 obj-$(CONFIG_MTD_L440GX)	+= l440gx.o
 obj-$(CONFIG_MTD_L440GX)	+= l440gx.o
 obj-$(CONFIG_MTD_AMD76XROM)	+= amd76xrom.o
 obj-$(CONFIG_MTD_AMD76XROM)	+= amd76xrom.o
+obj-$(CONFIG_MTD_ESB2ROM)	+= esb2rom.o
 obj-$(CONFIG_MTD_ICHXROM)	+= ichxrom.o
 obj-$(CONFIG_MTD_ICHXROM)	+= ichxrom.o
+obj-$(CONFIG_MTD_CK804XROM)	+= ck804xrom.o
 obj-$(CONFIG_MTD_TSUNAMI)	+= tsunami_flash.o
 obj-$(CONFIG_MTD_TSUNAMI)	+= tsunami_flash.o
 obj-$(CONFIG_MTD_LUBBOCK)	+= lubbock-flash.o
 obj-$(CONFIG_MTD_LUBBOCK)	+= lubbock-flash.o
 obj-$(CONFIG_MTD_MAINSTONE)	+= mainstone-flash.o
 obj-$(CONFIG_MTD_MAINSTONE)	+= mainstone-flash.o
@@ -25,6 +26,7 @@ obj-$(CONFIG_MTD_MBX860)	+= mbx860.o
 obj-$(CONFIG_MTD_CEIVA)		+= ceiva.o
 obj-$(CONFIG_MTD_CEIVA)		+= ceiva.o
 obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
 obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
 obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o
 obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o
+obj-$(CONFIG_MTD_PHYSMAP_OF)	+= physmap_of.o
 obj-$(CONFIG_MTD_PNC2000)	+= pnc2000.o
 obj-$(CONFIG_MTD_PNC2000)	+= pnc2000.o
 obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
 obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
 obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o
 obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o

+ 28 - 6
drivers/mtd/maps/amd76xrom.c

@@ -7,6 +7,7 @@
 
 
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/types.h>
+#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <asm/io.h>
 #include <asm/io.h>
@@ -44,6 +45,23 @@ struct amd76xrom_map_info {
 	char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
 	char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
 };
 };
 
 
+/* The 2 bits controlling the window size are often set to allow reading
+ * the BIOS, but too small to allow writing, since the lock registers are
+ * 4MiB lower in the address space than the data.
+ *
+ * This is intended to prevent flashing the bios, perhaps accidentally.
+ *
+ * This parameter allows the normal driver to over-ride the BIOS settings.
+ *
+ * The bits are 6 and 7.  If both bits are set, it is a 5MiB window.
+ * If only the 7 Bit is set, it is a 4MiB window.  Otherwise, a
+ * 64KiB window.
+ *
+ */
+static uint win_size_bits;
+module_param(win_size_bits, uint, 0);
+MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x43 byte, normally set by BIOS.");
+
 static struct amd76xrom_window amd76xrom_window = {
 static struct amd76xrom_window amd76xrom_window = {
 	.maps = LIST_HEAD_INIT(amd76xrom_window.maps),
 	.maps = LIST_HEAD_INIT(amd76xrom_window.maps),
 };
 };
@@ -95,6 +113,16 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
 	/* Remember the pci dev I find the window in - already have a ref */
 	/* Remember the pci dev I find the window in - already have a ref */
 	window->pdev = pdev;
 	window->pdev = pdev;
 
 
+	/* Enable the selected rom window.  This is often incorrectly
+	 * set up by the BIOS, and the 4MiB offset for the lock registers
+	 * requires the full 5MiB of window space.
+	 *
+	 * This 'write, then read' approach leaves the bits for
+	 * other uses of the hardware info.
+	 */
+	pci_read_config_byte(pdev, 0x43, &byte);
+	pci_write_config_byte(pdev, 0x43, byte | win_size_bits );
+
 	/* Assume the rom window is properly setup, and find it's size */
 	/* Assume the rom window is properly setup, and find it's size */
 	pci_read_config_byte(pdev, 0x43, &byte);
 	pci_read_config_byte(pdev, 0x43, &byte);
 	if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) {
 	if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) {
@@ -129,12 +157,6 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
 			(unsigned long long)window->rsrc.end);
 			(unsigned long long)window->rsrc.end);
 	}
 	}
 
 
-#if 0
-
-	/* Enable the selected rom window */
-	pci_read_config_byte(pdev, 0x43, &byte);
-	pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits);
-#endif
 
 
 	/* Enable writes through the rom window */
 	/* Enable writes through the rom window */
 	pci_read_config_byte(pdev, 0x40, &byte);
 	pci_read_config_byte(pdev, 0x40, &byte);

+ 1 - 1
drivers/mtd/maps/bast-flash.c

@@ -131,7 +131,7 @@ static int bast_flash_probe(struct platform_device *pdev)
 
 
 	info->map.phys = res->start;
 	info->map.phys = res->start;
 	info->map.size = res->end - res->start + 1;
 	info->map.size = res->end - res->start + 1;
-	info->map.name = pdev->dev.bus_id;	
+	info->map.name = pdev->dev.bus_id;
 	info->map.bankwidth = 2;
 	info->map.bankwidth = 2;
 
 
 	if (info->map.size > AREA_MAXSIZE)
 	if (info->map.size > AREA_MAXSIZE)

+ 1 - 2
drivers/mtd/maps/ceiva.c

@@ -122,10 +122,9 @@ static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info
 	/*
 	/*
 	 * Allocate the map_info structs in one go.
 	 * Allocate the map_info structs in one go.
 	 */
 	 */
-	maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
+	maps = kzalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
 	if (!maps)
 	if (!maps)
 		return -ENOMEM;
 		return -ENOMEM;
-	memset(maps, 0, sizeof(struct map_info) * nr);
 	/*
 	/*
 	 * Claim and then map the memory regions.
 	 * Claim and then map the memory regions.
 	 */
 	 */

+ 356 - 0
drivers/mtd/maps/ck804xrom.c

@@ -0,0 +1,356 @@
+/*
+ * ck804xrom.c
+ *
+ * Normal mappings of chips in physical memory
+ *
+ * Dave Olsen <dolsen@lnxi.com>
+ * Ryan Jackson <rjackson@lnxi.com>
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/version.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/cfi.h>
+#include <linux/mtd/flashchip.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/list.h>
+
+
+#define MOD_NAME KBUILD_BASENAME
+
+#define ADDRESS_NAME_LEN 18
+
+#define ROM_PROBE_STEP_SIZE (64*1024)
+
+struct ck804xrom_window {
+	void __iomem *virt;
+	unsigned long phys;
+	unsigned long size;
+	struct list_head maps;
+	struct resource rsrc;
+	struct pci_dev *pdev;
+};
+
+struct ck804xrom_map_info {
+	struct list_head list;
+	struct map_info map;
+	struct mtd_info *mtd;
+	struct resource rsrc;
+	char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
+};
+
+
+/* The 2 bits controlling the window size are often set to allow reading
+ * the BIOS, but too small to allow writing, since the lock registers are
+ * 4MiB lower in the address space than the data.
+ *
+ * This is intended to prevent flashing the bios, perhaps accidentally.
+ *
+ * This parameter allows the normal driver to override the BIOS settings.
+ *
+ * The bits are 6 and 7.  If both bits are set, it is a 5MiB window.
+ * If only the 7 Bit is set, it is a 4MiB window.  Otherwise, a
+ * 64KiB window.
+ *
+ */
+static uint win_size_bits = 0;
+module_param(win_size_bits, uint, 0);
+MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x88 byte, normally set by BIOS.");
+
+static struct ck804xrom_window ck804xrom_window = {
+	.maps = LIST_HEAD_INIT(ck804xrom_window.maps),
+};
+
+static void ck804xrom_cleanup(struct ck804xrom_window *window)
+{
+	struct ck804xrom_map_info *map, *scratch;
+	u8 byte;
+
+	if (window->pdev) {
+		/* Disable writes through the rom window */
+		pci_read_config_byte(window->pdev, 0x6d, &byte);
+		pci_write_config_byte(window->pdev, 0x6d, byte & ~1);
+	}
+
+	/* Free all of the mtd devices */
+	list_for_each_entry_safe(map, scratch, &window->maps, list) {
+		if (map->rsrc.parent)
+			release_resource(&map->rsrc);
+
+		del_mtd_device(map->mtd);
+		map_destroy(map->mtd);
+		list_del(&map->list);
+		kfree(map);
+	}
+	if (window->rsrc.parent)
+		release_resource(&window->rsrc);
+
+	if (window->virt) {
+		iounmap(window->virt);
+		window->virt = NULL;
+		window->phys = 0;
+		window->size = 0;
+	}
+	pci_dev_put(window->pdev);
+}
+
+
+static int __devinit ck804xrom_init_one (struct pci_dev *pdev,
+	const struct pci_device_id *ent)
+{
+	static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
+	u8 byte;
+	struct ck804xrom_window *window = &ck804xrom_window;
+	struct ck804xrom_map_info *map = NULL;
+	unsigned long map_top;
+
+	/* Remember the pci dev I find the window in */
+	window->pdev = pci_dev_get(pdev);
+
+	/* Enable the selected rom window.  This is often incorrectly
+	 * set up by the BIOS, and the 4MiB offset for the lock registers
+	 * requires the full 5MiB of window space.
+	 *
+	 * This 'write, then read' approach leaves the bits for
+	 * other uses of the hardware info.
+	 */
+        pci_read_config_byte(pdev, 0x88, &byte);
+        pci_write_config_byte(pdev, 0x88, byte | win_size_bits );
+
+
+	/* Assume the rom window is properly setup, and find it's size */
+	pci_read_config_byte(pdev, 0x88, &byte);
+
+	if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6)))
+		window->phys = 0xffb00000; /* 5MiB */
+	else if ((byte & (1<<7)) == (1<<7))
+		window->phys = 0xffc00000; /* 4MiB */
+	else
+		window->phys = 0xffff0000; /* 64KiB */
+
+	window->size = 0xffffffffUL - window->phys + 1UL;
+
+	/*
+	 * Try to reserve the window mem region.  If this fails then
+	 * it is likely due to a fragment of the window being
+	 * "reserved" by the BIOS.  In the case that the
+	 * request_mem_region() fails then once the rom size is
+	 * discovered we will try to reserve the unreserved fragment.
+	 */
+	window->rsrc.name = MOD_NAME;
+	window->rsrc.start = window->phys;
+	window->rsrc.end   = window->phys + window->size - 1;
+	window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	if (request_resource(&iomem_resource, &window->rsrc)) {
+		window->rsrc.parent = NULL;
+		printk(KERN_ERR MOD_NAME
+			" %s(): Unable to register resource"
+			" 0x%.016llx-0x%.016llx - kernel bug?\n",
+			__func__,
+			(unsigned long long)window->rsrc.start,
+			(unsigned long long)window->rsrc.end);
+	}
+
+
+	/* Enable writes through the rom window */
+	pci_read_config_byte(pdev, 0x6d, &byte);
+	pci_write_config_byte(pdev, 0x6d, byte | 1);
+
+	/* FIXME handle registers 0x80 - 0x8C the bios region locks */
+
+	/* For write accesses caches are useless */
+	window->virt = ioremap_nocache(window->phys, window->size);
+	if (!window->virt) {
+		printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
+			window->phys, window->size);
+		goto out;
+	}
+
+	/* Get the first address to look for a rom chip at */
+	map_top = window->phys;
+#if 1
+	/* The probe sequence run over the firmware hub lock
+	 * registers sets them to 0x7 (no access).
+	 * Probe at most the last 4MiB of the address space.
+	 */
+	if (map_top < 0xffc00000)
+		map_top = 0xffc00000;
+#endif
+	/* Loop  through and look for rom chips.  Since we don't know the
+	 * starting address for each chip, probe every ROM_PROBE_STEP_SIZE
+	 * bytes from the starting address of the window.
+	 */
+	while((map_top - 1) < 0xffffffffUL) {
+		struct cfi_private *cfi;
+		unsigned long offset;
+		int i;
+
+		if (!map)
+			map = kmalloc(sizeof(*map), GFP_KERNEL);
+
+		if (!map) {
+			printk(KERN_ERR MOD_NAME ": kmalloc failed");
+			goto out;
+		}
+		memset(map, 0, sizeof(*map));
+		INIT_LIST_HEAD(&map->list);
+		map->map.name = map->map_name;
+		map->map.phys = map_top;
+		offset = map_top - window->phys;
+		map->map.virt = (void __iomem *)
+			(((unsigned long)(window->virt)) + offset);
+		map->map.size = 0xffffffffUL - map_top + 1UL;
+		/* Set the name of the map to the address I am trying */
+		sprintf(map->map_name, "%s @%08lx",
+			MOD_NAME, map->map.phys);
+
+		/* There is no generic VPP support */
+		for(map->map.bankwidth = 32; map->map.bankwidth;
+			map->map.bankwidth >>= 1)
+		{
+			char **probe_type;
+			/* Skip bankwidths that are not supported */
+			if (!map_bankwidth_supported(map->map.bankwidth))
+				continue;
+
+			/* Setup the map methods */
+			simple_map_init(&map->map);
+
+			/* Try all of the probe methods */
+			probe_type = rom_probe_types;
+			for(; *probe_type; probe_type++) {
+				map->mtd = do_map_probe(*probe_type, &map->map);
+				if (map->mtd)
+					goto found;
+			}
+		}
+		map_top += ROM_PROBE_STEP_SIZE;
+		continue;
+	found:
+		/* Trim the size if we are larger than the map */
+		if (map->mtd->size > map->map.size) {
+			printk(KERN_WARNING MOD_NAME
+				" rom(%u) larger than window(%lu). fixing...\n",
+				map->mtd->size, map->map.size);
+			map->mtd->size = map->map.size;
+		}
+		if (window->rsrc.parent) {
+			/*
+			 * Registering the MTD device in iomem may not be possible
+			 * if there is a BIOS "reserved" and BUSY range.  If this
+			 * fails then continue anyway.
+			 */
+			map->rsrc.name  = map->map_name;
+			map->rsrc.start = map->map.phys;
+			map->rsrc.end   = map->map.phys + map->mtd->size - 1;
+			map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+			if (request_resource(&window->rsrc, &map->rsrc)) {
+				printk(KERN_ERR MOD_NAME
+					": cannot reserve MTD resource\n");
+				map->rsrc.parent = NULL;
+			}
+		}
+
+		/* Make the whole region visible in the map */
+		map->map.virt = window->virt;
+		map->map.phys = window->phys;
+		cfi = map->map.fldrv_priv;
+		for(i = 0; i < cfi->numchips; i++)
+			cfi->chips[i].start += offset;
+
+		/* Now that the mtd devices is complete claim and export it */
+		map->mtd->owner = THIS_MODULE;
+		if (add_mtd_device(map->mtd)) {
+			map_destroy(map->mtd);
+			map->mtd = NULL;
+			goto out;
+		}
+
+
+		/* Calculate the new value of map_top */
+		map_top += map->mtd->size;
+
+		/* File away the map structure */
+		list_add(&map->list, &window->maps);
+		map = NULL;
+	}
+
+ out:
+	/* Free any left over map structures */
+	if (map)
+		kfree(map);
+
+	/* See if I have any map structures */
+	if (list_empty(&window->maps)) {
+		ck804xrom_cleanup(window);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+static void __devexit ck804xrom_remove_one (struct pci_dev *pdev)
+{
+	struct ck804xrom_window *window = &ck804xrom_window;
+
+	ck804xrom_cleanup(window);
+}
+
+static struct pci_device_id ck804xrom_pci_tbl[] = {
+	{ PCI_VENDOR_ID_NVIDIA, 0x0051,
+        PCI_ANY_ID, PCI_ANY_ID, }, /* nvidia ck804 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, ck804xrom_pci_tbl);
+
+#if 0
+static struct pci_driver ck804xrom_driver = {
+	.name =		MOD_NAME,
+	.id_table =	ck804xrom_pci_tbl,
+	.probe =	ck804xrom_init_one,
+	.remove =	ck804xrom_remove_one,
+};
+#endif
+
+static int __init init_ck804xrom(void)
+{
+	struct pci_dev *pdev;
+	struct pci_device_id *id;
+	int retVal;
+	pdev = NULL;
+
+	for(id = ck804xrom_pci_tbl; id->vendor; id++) {
+		pdev = pci_find_device(id->vendor, id->device, NULL);
+		if (pdev)
+			break;
+	}
+	if (pdev) {
+		retVal = ck804xrom_init_one(pdev, &ck804xrom_pci_tbl[0]);
+		pci_dev_put(pdev);
+		return retVal;
+	}
+	return -ENXIO;
+#if 0
+	return pci_module_init(&ck804xrom_driver);
+#endif
+}
+
+static void __exit cleanup_ck804xrom(void)
+{
+	ck804xrom_remove_one(ck804xrom_window.pdev);
+}
+
+module_init(init_ck804xrom);
+module_exit(cleanup_ck804xrom);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>, Dave Olsen <dolsen@lnxi.com>");
+MODULE_DESCRIPTION("MTD map driver for BIOS chips on the Nvidia ck804 southbridge");
+

+ 0 - 283
drivers/mtd/maps/cstm_mips_ixx.c

@@ -1,283 +0,0 @@
-/*
- * $Id: cstm_mips_ixx.c,v 1.14 2005/11/07 11:14:26 gleixner Exp $
- *
- * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
- * Config with both CFI and JEDEC device support.
- *
- * Basically physmap.c with the addition of partitions and
- * an array of mapping info to accomodate more than one flash type per board.
- *
- * Copyright 2000 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 as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
- *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
- *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
- *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
- *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
- *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *  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.,
- *  675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#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>
-#include <linux/delay.h>
-
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-#define CC_GCR             0xB4013818
-#define CC_GPBCR           0xB401380A
-#define CC_GPBDR           0xB4013808
-#define CC_M68K_DEVICE     1
-#define CC_M68K_FUNCTION   6
-#define CC_CONFADDR        0xB8004000
-#define CC_CONFDATA        0xB8004004
-#define CC_FC_FCR          0xB8002004
-#define CC_FC_DCR          0xB8002008
-#define CC_GPACR           0xB4013802
-#define CC_GPAICR          0xB4013804
-#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
-
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp)
-{
-	static DEFINE_SPINLOCK(vpp_lock);
-	static int vpp_count = 0;
-	unsigned long flags;
-
-	spin_lock_irqsave(&vpp_lock, flags);
-
-	if (vpp) {
-		if (!vpp_count++) {
-			__u16	data;
-			__u8	data1;
-			static u8 first = 1;
-
-			// Set GPIO port B pin3 to high
-			data = *(__u16 *)(CC_GPBCR);
-			data = (data & 0xff0f) | 0x0040;
-			*(__u16 *)CC_GPBCR = data;
-			*(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08;
-			if (first) {
-				first = 0;
-				/* need to have this delay for first
-				   enabling vpp after powerup */
-				udelay(40);
-			}
-		}
-	} else {
-		if (!--vpp_count) {
-			__u16	data;
-
-			// Set GPIO port B pin3 to high
-			data = *(__u16 *)(CC_GPBCR);
-			data = (data & 0xff3f) | 0x0040;
-			*(__u16 *)CC_GPBCR = data;
-			*(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7;
-		}
-	}
-	spin_unlock_irqrestore(&vpp_lock, flags);
-}
-#endif
-
-/* board and partition description */
-
-#define MAX_PHYSMAP_PARTITIONS    8
-struct cstm_mips_ixx_info {
-	char *name;
-	unsigned long window_addr;
-	unsigned long window_size;
-	int bankwidth;
-	int num_partitions;
-};
-
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-#define PHYSMAP_NUMBER  1  // number of board desc structs needed, one per contiguous flash type
-const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] =
-{
-    {   // 28F128J3A in 2x16 configuration
-        "big flash",     // name
-	0x08000000,      // window_addr
-	0x02000000,      // window_size
-        4,               // bankwidth
-	1,               // num_partitions
-    }
-
-};
-static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
-{   // 28F128J3A in 2x16 configuration
-	{
-		.name = "main partition ",
-		.size = 0x02000000, // 128 x 2 x 128k byte sectors
-		.offset = 0,
-	},
-},
-};
-#else /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
-#define PHYSMAP_NUMBER  1  // number of board desc structs needed, one per contiguous flash type
-const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] =
-{
-    {
-        "MTD flash",                   // name
-	CONFIG_MTD_CSTM_MIPS_IXX_START,      // window_addr
-	CONFIG_MTD_CSTM_MIPS_IXX_LEN,        // window_size
-        CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH,   // bankwidth
-	1,                             // num_partitions
-    },
-
-};
-static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
-{
-	{
-		.name = "main partition",
-		.size =  CONFIG_MTD_CSTM_MIPS_IXX_LEN,
-		.offset = 0,
-	},
-},
-};
-#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
-
-struct map_info cstm_mips_ixx_map[PHYSMAP_NUMBER];
-
-int __init init_cstm_mips_ixx(void)
-{
-	int i;
-	int jedec;
-        struct mtd_info *mymtd;
-        struct mtd_partition *parts;
-
-	/* Initialize mapping */
-	for (i=0;i<PHYSMAP_NUMBER;i++) {
-		printk(KERN_NOTICE "cstm_mips_ixx flash device: 0x%lx at 0x%lx\n",
-		       cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
-
-
-		cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr;
-		cstm_mips_ixx_map[i].virt = ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
-		if (!cstm_mips_ixx_map[i].virt) {
-			int j = 0;
-			printk(KERN_WARNING "Failed to ioremap\n");
-			for (j = 0; j < i; j++) {
-				if (cstm_mips_ixx_map[j].virt) {
-					iounmap(cstm_mips_ixx_map[j].virt);
-					cstm_mips_ixx_map[j].virt = NULL;
-				}
-			}
-			return -EIO;
-	        }
-		cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name;
-		cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size;
-		cstm_mips_ixx_map[i].bankwidth = cstm_mips_ixx_board_desc[i].bankwidth;
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-                cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp;
-#endif
-		simple_map_init(&cstm_mips_ixx_map[i]);
-		//printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt));
-	}
-
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-        setup_ITE_IVR_flash();
-#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
-
-	for (i=0;i<PHYSMAP_NUMBER;i++) {
-                parts = &cstm_mips_ixx_partitions[i][0];
-		jedec = 0;
-		mymtd = (struct mtd_info *)do_map_probe("cfi_probe", &cstm_mips_ixx_map[i]);
-		//printk(KERN_NOTICE "phymap %d cfi_probe: mymtd is %x\n",i,(unsigned int)mymtd);
-		if (!mymtd) {
-			jedec = 1;
-			mymtd = (struct mtd_info *)do_map_probe("jedec", &cstm_mips_ixx_map[i]);
-		        printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd);
-		}
-		if (mymtd) {
-			mymtd->owner = THIS_MODULE;
-
-	                cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd;
-		        add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions);
-		}
-		else {
-			for (i = 0; i < PHYSMAP_NUMBER; i++) {
-				if (cstm_mips_ixx_map[i].virt) {
-					iounmap(cstm_mips_ixx_map[i].virt);
-					cstm_mips_ixx_map[i].virt = NULL;
-				}
-			}
-			return -ENXIO;
-		}
-	}
-	return 0;
-}
-
-static void __exit cleanup_cstm_mips_ixx(void)
-{
-	int i;
-        struct mtd_info *mymtd;
-
-	for (i=0;i<PHYSMAP_NUMBER;i++) {
-	        mymtd = (struct mtd_info *)cstm_mips_ixx_map[i].map_priv_2;
-		if (mymtd) {
-			del_mtd_partitions(mymtd);
-			map_destroy(mymtd);
-		}
-		if (cstm_mips_ixx_map[i].virt) {
-			iounmap((void *)cstm_mips_ixx_map[i].virt);
-			cstm_mips_ixx_map[i].virt = 0;
-		}
-	}
-}
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-void PCISetULongByOffset(__u32 DevNumber, __u32 FuncNumber, __u32 Offset, __u32 data)
-{
-	__u32	offset;
-
-	offset = ( unsigned long )( 0x80000000 | ( DevNumber << 11 ) + ( FuncNumber << 8 ) + Offset) ;
-
-	*(__u32 *)CC_CONFADDR = offset;
-	*(__u32 *)CC_CONFDATA = data;
-}
-void setup_ITE_IVR_flash()
-{
-		__u32	size, base;
-
-		size = 0x0e000000;		// 32MiB
-		base = (0x08000000) >> 8 >>1; // Bug: we must shift one more bit
-
-		/* need to set ITE flash to 32 bits instead of default 8 */
-#ifdef CONFIG_MIPS_IVR
-		*(__u32 *)CC_FC_FCR = 0x55;
-		*(__u32 *)CC_GPACR = 0xfffc;
-#else
-		*(__u32 *)CC_FC_FCR = 0x77;
-#endif
-		/* turn bursting off */
-		*(__u32 *)CC_FC_DCR = 0x0;
-
-		/* setup for one chip 4 byte PCI access */
-		PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x60, size | base);
-		PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x64, 0x02);
-}
-#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
-
-module_init(init_cstm_mips_ixx);
-module_exit(cleanup_cstm_mips_ixx);
-
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
-MODULE_DESCRIPTION("MTD map driver for ITE 8172G and Globespan IVR boards");

+ 450 - 0
drivers/mtd/maps/esb2rom.c

@@ -0,0 +1,450 @@
+/*
+ * esb2rom.c
+ *
+ * Normal mappings of flash chips in physical memory
+ * through the Intel ESB2 Southbridge.
+ *
+ * This was derived from ichxrom.c in May 2006 by
+ *	Lew Glendenning <lglendenning@lnxi.com>
+ *
+ * Eric Biederman, of course, was a major help in this effort.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/version.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/cfi.h>
+#include <linux/mtd/flashchip.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/list.h>
+
+#define MOD_NAME KBUILD_BASENAME
+
+#define ADDRESS_NAME_LEN 18
+
+#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
+
+#define BIOS_CNTL	0xDC
+#define BIOS_LOCK_ENABLE	0x02
+#define BIOS_WRITE_ENABLE	0x01
+
+/* This became a 16-bit register, and EN2 has disappeared */
+#define FWH_DEC_EN1	0xD8
+#define FWH_F8_EN	0x8000
+#define FWH_F0_EN	0x4000
+#define FWH_E8_EN	0x2000
+#define FWH_E0_EN	0x1000
+#define FWH_D8_EN	0x0800
+#define FWH_D0_EN	0x0400
+#define FWH_C8_EN	0x0200
+#define FWH_C0_EN	0x0100
+#define FWH_LEGACY_F_EN	0x0080
+#define FWH_LEGACY_E_EN	0x0040
+/* reserved  0x0020 and 0x0010 */
+#define FWH_70_EN	0x0008
+#define FWH_60_EN	0x0004
+#define FWH_50_EN	0x0002
+#define FWH_40_EN	0x0001
+
+/* these are 32-bit values */
+#define FWH_SEL1	0xD0
+#define FWH_SEL2	0xD4
+
+#define FWH_8MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
+			 FWH_70_EN | FWH_60_EN | FWH_50_EN | FWH_40_EN)
+
+#define FWH_7MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
+			 FWH_70_EN | FWH_60_EN | FWH_50_EN)
+
+#define FWH_6MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
+			 FWH_70_EN | FWH_60_EN)
+
+#define FWH_5MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
+			 FWH_70_EN)
+
+#define FWH_4MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN)
+
+#define FWH_3_5MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN)
+
+#define FWH_3MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+			 FWH_D8_EN | FWH_D0_EN)
+
+#define FWH_2_5MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+			 FWH_D8_EN)
+
+#define FWH_2MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN)
+
+#define FWH_1_5MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN)
+
+#define FWH_1MiB	(FWH_F8_EN | FWH_F0_EN)
+
+#define FWH_0_5MiB	(FWH_F8_EN)
+
+
+struct esb2rom_window {
+	void __iomem* virt;
+	unsigned long phys;
+	unsigned long size;
+	struct list_head maps;
+	struct resource rsrc;
+	struct pci_dev *pdev;
+};
+
+struct esb2rom_map_info {
+	struct list_head list;
+	struct map_info map;
+	struct mtd_info *mtd;
+	struct resource rsrc;
+	char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
+};
+
+static struct esb2rom_window esb2rom_window = {
+	.maps = LIST_HEAD_INIT(esb2rom_window.maps),
+};
+
+static void esb2rom_cleanup(struct esb2rom_window *window)
+{
+	struct esb2rom_map_info *map, *scratch;
+	u8 byte;
+
+	/* Disable writes through the rom window */
+	pci_read_config_byte(window->pdev, BIOS_CNTL, &byte);
+	pci_write_config_byte(window->pdev, BIOS_CNTL,
+		byte & ~BIOS_WRITE_ENABLE);
+
+	/* Free all of the mtd devices */
+	list_for_each_entry_safe(map, scratch, &window->maps, list) {
+		if (map->rsrc.parent)
+			release_resource(&map->rsrc);
+		del_mtd_device(map->mtd);
+		map_destroy(map->mtd);
+		list_del(&map->list);
+		kfree(map);
+	}
+	if (window->rsrc.parent)
+		release_resource(&window->rsrc);
+	if (window->virt) {
+		iounmap(window->virt);
+		window->virt = NULL;
+		window->phys = 0;
+		window->size = 0;
+	}
+	pci_dev_put(window->pdev);
+}
+
+static int __devinit esb2rom_init_one(struct pci_dev *pdev,
+				const struct pci_device_id *ent)
+{
+	static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
+	struct esb2rom_window *window = &esb2rom_window;
+	struct esb2rom_map_info *map = NULL;
+	unsigned long map_top;
+	u8 byte;
+	u16 word;
+
+	/* For now I just handle the ecb2 and I assume there
+	 * are not a lot of resources up at the top of the address
+	 * space.  It is possible to handle other devices in the
+	 * top 16MiB but it is very painful.  Also since
+	 * you can only really attach a FWH to an ICHX there
+	 * a number of simplifications you can make.
+	 *
+	 * Also you can page firmware hubs if an 8MiB window isn't enough
+	 * but don't currently handle that case either.
+	 */
+	window->pdev = pci_dev_get(pdev);
+
+	/* RLG:  experiment 2.  Force the window registers to the widest values */
+
+/*
+	pci_read_config_word(pdev, FWH_DEC_EN1, &word);
+	printk(KERN_DEBUG "Original FWH_DEC_EN1 : %x\n", word);
+	pci_write_config_byte(pdev, FWH_DEC_EN1, 0xff);
+	pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);
+	printk(KERN_DEBUG "New FWH_DEC_EN1 : %x\n", byte);
+
+	pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
+	printk(KERN_DEBUG "Original FWH_DEC_EN2 : %x\n", byte);
+	pci_write_config_byte(pdev, FWH_DEC_EN2, 0x0f);
+	pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
+	printk(KERN_DEBUG "New FWH_DEC_EN2 : %x\n", byte);
+*/
+
+	/* Find a region continuous to the end of the ROM window  */
+	window->phys = 0;
+	pci_read_config_word(pdev, FWH_DEC_EN1, &word);
+	printk(KERN_DEBUG "pci_read_config_byte : %x\n", word);
+
+	if ((word & FWH_8MiB) == FWH_8MiB)
+		window->phys = 0xff400000;
+	else if ((word & FWH_7MiB) == FWH_7MiB)
+		window->phys = 0xff500000;
+	else if ((word & FWH_6MiB) == FWH_6MiB)
+		window->phys = 0xff600000;
+	else if ((word & FWH_5MiB) == FWH_5MiB)
+		window->phys = 0xFF700000;
+	else if ((word & FWH_4MiB) == FWH_4MiB)
+		window->phys = 0xffc00000;
+	else if ((word & FWH_3_5MiB) == FWH_3_5MiB)
+		window->phys = 0xffc80000;
+	else if ((word & FWH_3MiB) == FWH_3MiB)
+		window->phys = 0xffd00000;
+	else if ((word & FWH_2_5MiB) == FWH_2_5MiB)
+		window->phys = 0xffd80000;
+	else if ((word & FWH_2MiB) == FWH_2MiB)
+		window->phys = 0xffe00000;
+	else if ((word & FWH_1_5MiB) == FWH_1_5MiB)
+		window->phys = 0xffe80000;
+	else if ((word & FWH_1MiB) == FWH_1MiB)
+		window->phys = 0xfff00000;
+	else if ((word & FWH_0_5MiB) == FWH_0_5MiB)
+		window->phys = 0xfff80000;
+
+	/* reserved  0x0020 and 0x0010 */
+	window->phys -= 0x400000UL;
+	window->size = (0xffffffffUL - window->phys) + 1UL;
+
+	/* Enable writes through the rom window */
+	pci_read_config_byte(pdev, BIOS_CNTL, &byte);
+	if (!(byte & BIOS_WRITE_ENABLE)  && (byte & (BIOS_LOCK_ENABLE))) {
+		/* The BIOS will generate an error if I enable
+		 * this device, so don't even try.
+		 */
+		printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
+		goto out;
+	}
+	pci_write_config_byte(pdev, BIOS_CNTL, byte | BIOS_WRITE_ENABLE);
+
+	/*
+	 * Try to reserve the window mem region.  If this fails then
+	 * it is likely due to the window being "reseved" by the BIOS.
+	 */
+	window->rsrc.name = MOD_NAME;
+	window->rsrc.start = window->phys;
+	window->rsrc.end   = window->phys + window->size - 1;
+	window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	if (request_resource(&iomem_resource, &window->rsrc)) {
+		window->rsrc.parent = NULL;
+		printk(KERN_DEBUG MOD_NAME
+			": %s(): Unable to register resource"
+			" 0x%.08llx-0x%.08llx - kernel bug?\n",
+			__func__,
+			(unsigned long long)window->rsrc.start,
+			(unsigned long long)window->rsrc.end);
+	}
+
+	/* Map the firmware hub into my address space. */
+	window->virt = ioremap_nocache(window->phys, window->size);
+	if (!window->virt) {
+		printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
+			window->phys, window->size);
+		goto out;
+	}
+
+	/* Get the first address to look for an rom chip at */
+	map_top = window->phys;
+	if ((window->phys & 0x3fffff) != 0) {
+		/* if not aligned on 4MiB, look 4MiB lower in address space */
+		map_top = window->phys + 0x400000;
+	}
+#if 1
+	/* The probe sequence run over the firmware hub lock
+	 * registers sets them to 0x7 (no access).
+	 * (Insane hardware design, but most copied Intel's.)
+	 * ==> Probe at most the last 4M of the address space.
+	 */
+	if (map_top < 0xffc00000)
+		map_top = 0xffc00000;
+#endif
+	/* Loop through and look for rom chips */
+	while ((map_top - 1) < 0xffffffffUL) {
+		struct cfi_private *cfi;
+		unsigned long offset;
+		int i;
+
+		if (!map)
+			map = kmalloc(sizeof(*map), GFP_KERNEL);
+		if (!map) {
+			printk(KERN_ERR MOD_NAME ": kmalloc failed");
+			goto out;
+		}
+		memset(map, 0, sizeof(*map));
+		INIT_LIST_HEAD(&map->list);
+		map->map.name = map->map_name;
+		map->map.phys = map_top;
+		offset = map_top - window->phys;
+		map->map.virt = (void __iomem *)
+			(((unsigned long)(window->virt)) + offset);
+		map->map.size = 0xffffffffUL - map_top + 1UL;
+		/* Set the name of the map to the address I am trying */
+		sprintf(map->map_name, "%s @%08lx",
+			MOD_NAME, map->map.phys);
+
+		/* Firmware hubs only use vpp when being programmed
+		 * in a factory setting.  So in-place programming
+		 * needs to use a different method.
+		 */
+		for(map->map.bankwidth = 32; map->map.bankwidth;
+			map->map.bankwidth >>= 1) {
+			char **probe_type;
+			/* Skip bankwidths that are not supported */
+			if (!map_bankwidth_supported(map->map.bankwidth))
+				continue;
+
+			/* Setup the map methods */
+			simple_map_init(&map->map);
+
+			/* Try all of the probe methods */
+			probe_type = rom_probe_types;
+			for(; *probe_type; probe_type++) {
+				map->mtd = do_map_probe(*probe_type, &map->map);
+				if (map->mtd)
+					goto found;
+			}
+		}
+		map_top += ROM_PROBE_STEP_SIZE;
+		continue;
+	found:
+		/* Trim the size if we are larger than the map */
+		if (map->mtd->size > map->map.size) {
+			printk(KERN_WARNING MOD_NAME
+				" rom(%u) larger than window(%lu). fixing...\n",
+				map->mtd->size, map->map.size);
+			map->mtd->size = map->map.size;
+		}
+		if (window->rsrc.parent) {
+			/*
+			 * Registering the MTD device in iomem may not be possible
+			 * if there is a BIOS "reserved" and BUSY range.  If this
+			 * fails then continue anyway.
+			 */
+			map->rsrc.name  = map->map_name;
+			map->rsrc.start = map->map.phys;
+			map->rsrc.end   = map->map.phys + map->mtd->size - 1;
+			map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+			if (request_resource(&window->rsrc, &map->rsrc)) {
+				printk(KERN_ERR MOD_NAME
+					": cannot reserve MTD resource\n");
+				map->rsrc.parent = NULL;
+			}
+		}
+
+		/* Make the whole region visible in the map */
+		map->map.virt = window->virt;
+		map->map.phys = window->phys;
+		cfi = map->map.fldrv_priv;
+		for(i = 0; i < cfi->numchips; i++)
+			cfi->chips[i].start += offset;
+
+		/* Now that the mtd devices is complete claim and export it */
+		map->mtd->owner = THIS_MODULE;
+		if (add_mtd_device(map->mtd)) {
+			map_destroy(map->mtd);
+			map->mtd = NULL;
+			goto out;
+		}
+
+		/* Calculate the new value of map_top */
+		map_top += map->mtd->size;
+
+		/* File away the map structure */
+		list_add(&map->list, &window->maps);
+		map = NULL;
+	}
+
+ out:
+	/* Free any left over map structures */
+	kfree(map);
+
+	/* See if I have any map structures */
+	if (list_empty(&window->maps)) {
+		esb2rom_cleanup(window);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __devexit esb2rom_remove_one (struct pci_dev *pdev)
+{
+	struct esb2rom_window *window = &esb2rom_window;
+	esb2rom_cleanup(window);
+}
+
+static struct pci_device_id esb2rom_pci_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0,
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, },
+};
+
+#if 0
+MODULE_DEVICE_TABLE(pci, esb2rom_pci_tbl);
+
+static struct pci_driver esb2rom_driver = {
+	.name =		MOD_NAME,
+	.id_table =	esb2rom_pci_tbl,
+	.probe =	esb2rom_init_one,
+	.remove =	esb2rom_remove_one,
+};
+#endif
+
+static int __init init_esb2rom(void)
+{
+	struct pci_dev *pdev;
+	struct pci_device_id *id;
+	int retVal;
+
+	pdev = NULL;
+	for (id = esb2rom_pci_tbl; id->vendor; id++) {
+		printk(KERN_DEBUG "device id = %x\n", id->device);
+		pdev = pci_get_device(id->vendor, id->device, NULL);
+		if (pdev) {
+			printk(KERN_DEBUG "matched device = %x\n", id->device);
+			break;
+		}
+	}
+	if (pdev) {
+		printk(KERN_DEBUG "matched device id %x\n", id->device);
+		retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]);
+		pci_dev_put(pdev);
+		printk(KERN_DEBUG "retVal = %d\n", retVal);
+		return retVal;
+	}
+	return -ENXIO;
+#if 0
+	return pci_register_driver(&esb2rom_driver);
+#endif
+}
+
+static void __exit cleanup_esb2rom(void)
+{
+	esb2rom_remove_one(esb2rom_window.pdev);
+}
+
+module_init(init_esb2rom);
+module_exit(cleanup_esb2rom);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lew Glendenning <lglendenning@lnxi.com>");
+MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ESB2 southbridge");

+ 1 - 3
drivers/mtd/maps/integrator-flash.c

@@ -75,14 +75,12 @@ static int armflash_probe(struct platform_device *dev)
 	int err;
 	int err;
 	void __iomem *base;
 	void __iomem *base;
 
 
-	info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL);
+	info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL);
 	if (!info) {
 	if (!info) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto out;
 		goto out;
 	}
 	}
 
 
-	memset(info, 0, sizeof(struct armflash_info));
-
 	info->plat = plat;
 	info->plat = plat;
 	if (plat && plat->init) {
 	if (plat && plat->init) {
 		err = plat->init();
 		err = plat->init();

+ 3 - 2
drivers/mtd/maps/nettel.c

@@ -20,6 +20,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/cfi.h>
 #include <linux/reboot.h>
 #include <linux/reboot.h>
+#include <linux/err.h>
 #include <linux/kdev_t.h>
 #include <linux/kdev_t.h>
 #include <linux/root_dev.h>
 #include <linux/root_dev.h>
 #include <asm/io.h>
 #include <asm/io.h>
@@ -178,7 +179,7 @@ int nettel_eraseconfig(void)
 
 
 	init_waitqueue_head(&wait_q);
 	init_waitqueue_head(&wait_q);
 	mtd = get_mtd_device(NULL, 2);
 	mtd = get_mtd_device(NULL, 2);
-	if (mtd) {
+	if (!IS_ERR(mtd)) {
 		nettel_erase.mtd = mtd;
 		nettel_erase.mtd = mtd;
 		nettel_erase.callback = nettel_erasecallback;
 		nettel_erase.callback = nettel_erasecallback;
 		nettel_erase.callback = NULL;
 		nettel_erase.callback = NULL;
@@ -471,7 +472,7 @@ out_unmap2:
 	iounmap(nettel_amd_map.virt);
 	iounmap(nettel_amd_map.virt);
 
 
 	return(rc);
 	return(rc);
-		
+
 }
 }
 
 
 /****************************************************************************/
 /****************************************************************************/

+ 1 - 3
drivers/mtd/maps/omap_nor.c

@@ -78,12 +78,10 @@ static int __devinit omapflash_probe(struct platform_device *pdev)
 	struct resource *res = pdev->resource;
 	struct resource *res = pdev->resource;
 	unsigned long size = res->end - res->start + 1;
 	unsigned long size = res->end - res->start + 1;
 
 
-	info = kmalloc(sizeof(struct omapflash_info), GFP_KERNEL);
+	info = kzalloc(sizeof(struct omapflash_info), GFP_KERNEL);
 	if (!info)
 	if (!info)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(info, 0, sizeof(struct omapflash_info));
-
 	if (!request_mem_region(res->start, size, "flash")) {
 	if (!request_mem_region(res->start, size, "flash")) {
 		err = -EBUSY;
 		err = -EBUSY;
 		goto out_free_info;
 		goto out_free_info;

+ 1 - 2
drivers/mtd/maps/pcmciamtd.c

@@ -735,11 +735,10 @@ static int pcmciamtd_probe(struct pcmcia_device *link)
 	struct pcmciamtd_dev *dev;
 	struct pcmciamtd_dev *dev;
 
 
 	/* Create new memory card device */
 	/* Create new memory card device */
-	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 	if (!dev) return -ENOMEM;
 	if (!dev) return -ENOMEM;
 	DEBUG(1, "dev=0x%p", dev);
 	DEBUG(1, "dev=0x%p", dev);
 
 
-	memset(dev, 0, sizeof(*dev));
 	dev->p_dev = link;
 	dev->p_dev = link;
 	link->priv = dev;
 	link->priv = dev;
 
 

+ 2 - 3
drivers/mtd/maps/physmap.c

@@ -89,15 +89,14 @@ static int physmap_flash_probe(struct platform_device *dev)
 		return -ENODEV;
 		return -ENODEV;
 
 
        	printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
        	printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
-	    (unsigned long long)dev->resource->end - dev->resource->start + 1,
+	    (unsigned long long)(dev->resource->end - dev->resource->start + 1),
 	    (unsigned long long)dev->resource->start);
 	    (unsigned long long)dev->resource->start);
 
 
-	info = kmalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
+	info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
 	if (info == NULL) {
 	if (info == NULL) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto err_out;
 		goto err_out;
 	}
 	}
-	memset(info, 0, sizeof(*info));
 
 
 	platform_set_drvdata(dev, info);
 	platform_set_drvdata(dev, info);
 
 

+ 255 - 0
drivers/mtd/maps/physmap_of.c

@@ -0,0 +1,255 @@
+/*
+ * Normal mappings of chips in physical memory for OF devices
+ *
+ * Copyright (C) 2006 MontaVista Software Inc.
+ * Author: Vitaly Wool <vwool@ru.mvista.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
+#include <asm/of_platform.h>
+
+struct physmap_flash_info {
+	struct mtd_info		*mtd;
+	struct map_info		map;
+	struct resource		*res;
+#ifdef CONFIG_MTD_PARTITIONS
+	int			nr_parts;
+	struct mtd_partition	*parts;
+#endif
+};
+
+static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
+#endif
+
+#ifdef CONFIG_MTD_PARTITIONS
+static int parse_flash_partitions(struct device_node *node,
+		struct mtd_partition **parts)
+{
+	int i, plen, retval = -ENOMEM;
+	const  u32  *part;
+	const  char *name;
+
+	part = get_property(node, "partitions", &plen);
+	if (part == NULL)
+		goto err;
+
+	retval = plen / (2 * sizeof(u32));
+	*parts = kzalloc(retval * sizeof(struct mtd_partition), GFP_KERNEL);
+	if (*parts == NULL) {
+		printk(KERN_ERR "Can't allocate the flash partition data!\n");
+		goto err;
+	}
+
+	name = get_property(node, "partition-names", &plen);
+
+	for (i = 0; i < retval; i++) {
+		(*parts)[i].offset = *part++;
+		(*parts)[i].size   = *part & ~1;
+		if (*part++ & 1) /* bit 0 set signifies read only partition */
+			(*parts)[i].mask_flags = MTD_WRITEABLE;
+
+		if (name != NULL && plen > 0) {
+			int len = strlen(name) + 1;
+
+			(*parts)[i].name = (char *)name;
+			plen -= len;
+			name += len;
+		} else
+			(*parts)[i].name = "unnamed";
+	}
+err:
+	return retval;
+}
+#endif
+
+static int of_physmap_remove(struct of_device *dev)
+{
+	struct physmap_flash_info *info;
+
+	info = dev_get_drvdata(&dev->dev);
+	if (info == NULL)
+		return 0;
+	dev_set_drvdata(&dev->dev, NULL);
+
+	if (info->mtd != NULL) {
+#ifdef CONFIG_MTD_PARTITIONS
+		if (info->nr_parts) {
+			del_mtd_partitions(info->mtd);
+			kfree(info->parts);
+		} else {
+			del_mtd_device(info->mtd);
+		}
+#else
+		del_mtd_device(info->mtd);
+#endif
+		map_destroy(info->mtd);
+	}
+
+	if (info->map.virt != NULL)
+		iounmap(info->map.virt);
+
+	if (info->res != NULL) {
+		release_resource(info->res);
+		kfree(info->res);
+	}
+
+	return 0;
+}
+
+static int __devinit of_physmap_probe(struct of_device *dev, const struct of_device_id *match)
+{
+	struct device_node *dp = dev->node;
+	struct resource res;
+	struct physmap_flash_info *info;
+	const char **probe_type;
+	const char *of_probe;
+	const u32 *width;
+	int err;
+
+
+	if (of_address_to_resource(dp, 0, &res)) {
+		dev_err(&dev->dev, "Can't get the flash mapping!\n");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+       	dev_dbg(&dev->dev, "physmap flash device: %.8llx at %.8llx\n",
+	    (unsigned long long)res.end - res.start + 1,
+	    (unsigned long long)res.start);
+
+	info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
+	if (info == NULL) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+	memset(info, 0, sizeof(*info));
+
+	dev_set_drvdata(&dev->dev, info);
+
+	info->res = request_mem_region(res.start, res.end - res.start + 1,
+			dev->dev.bus_id);
+	if (info->res == NULL) {
+		dev_err(&dev->dev, "Could not reserve memory region\n");
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	width = get_property(dp, "bank-width", NULL);
+	if (width == NULL) {
+		dev_err(&dev->dev, "Can't get the flash bank width!\n");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	info->map.name = dev->dev.bus_id;
+	info->map.phys = res.start;
+	info->map.size = res.end - res.start + 1;
+	info->map.bankwidth = *width;
+
+	info->map.virt = ioremap(info->map.phys, info->map.size);
+	if (info->map.virt == NULL) {
+		dev_err(&dev->dev, "Failed to ioremap flash region\n");
+		err = EIO;
+		goto err_out;
+	}
+
+	simple_map_init(&info->map);
+
+	of_probe = get_property(dp, "probe-type", NULL);
+	if (of_probe == NULL) {
+		probe_type = rom_probe_types;
+		for (; info->mtd == NULL && *probe_type != NULL; probe_type++)
+			info->mtd = do_map_probe(*probe_type, &info->map);
+	} else if (!strcmp(of_probe, "CFI"))
+		info->mtd = do_map_probe("cfi_probe", &info->map);
+	else if (!strcmp(of_probe, "JEDEC"))
+		info->mtd = do_map_probe("jedec_probe", &info->map);
+	else {
+ 		if (strcmp(of_probe, "ROM"))
+			dev_dbg(&dev->dev, "map_probe: don't know probe type "
+			"'%s', mapping as rom\n");
+		info->mtd = do_map_probe("mtd_rom", &info->map);
+	}
+	if (info->mtd == NULL) {
+		dev_err(&dev->dev, "map_probe failed\n");
+		err = -ENXIO;
+		goto err_out;
+	}
+	info->mtd->owner = THIS_MODULE;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0);
+	if (err > 0) {
+		add_mtd_partitions(info->mtd, info->parts, err);
+	} else if ((err = parse_flash_partitions(dp, &info->parts)) > 0) {
+		dev_info(&dev->dev, "Using OF partition information\n");
+		add_mtd_partitions(info->mtd, info->parts, err);
+		info->nr_parts = err;
+	} else
+#endif
+
+	add_mtd_device(info->mtd);
+	return 0;
+
+err_out:
+	of_physmap_remove(dev);
+	return err;
+
+	return 0;
+
+
+}
+
+static struct of_device_id of_physmap_match[] = {
+	{
+		.type		= "rom",
+		.compatible	= "direct-mapped"
+	},
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, of_physmap_match);
+
+
+static struct of_platform_driver of_physmap_flash_driver = {
+	.name		= "physmap-flash",
+	.match_table	= of_physmap_match,
+	.probe		= of_physmap_probe,
+	.remove		= of_physmap_remove,
+};
+
+static int __init of_physmap_init(void)
+{
+	return of_register_platform_driver(&of_physmap_flash_driver);
+}
+
+static void __exit of_physmap_exit(void)
+{
+	of_unregister_platform_driver(&of_physmap_flash_driver);
+}
+
+module_init(of_physmap_init);
+module_exit(of_physmap_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
+MODULE_DESCRIPTION("Configurable MTD map driver for OF");

+ 1 - 2
drivers/mtd/maps/plat-ram.c

@@ -147,14 +147,13 @@ static int platram_probe(struct platform_device *pdev)
 
 
 	pdata = pdev->dev.platform_data;
 	pdata = pdev->dev.platform_data;
 
 
-	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	if (info == NULL) {
 	if (info == NULL) {
 		dev_err(&pdev->dev, "no memory for flash info\n");
 		dev_err(&pdev->dev, "no memory for flash info\n");
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto exit_error;
 		goto exit_error;
 	}
 	}
 
 
-	memset(info, 0, sizeof(*info));
 	platform_set_drvdata(pdev, info);
 	platform_set_drvdata(pdev, info);
 
 
 	info->dev = &pdev->dev;
 	info->dev = &pdev->dev;

+ 1 - 3
drivers/mtd/maps/sa1100-flash.c

@@ -273,14 +273,12 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
 	/*
 	/*
 	 * Allocate the map_info structs in one go.
 	 * Allocate the map_info structs in one go.
 	 */
 	 */
-	info = kmalloc(size, GFP_KERNEL);
+	info = kzalloc(size, GFP_KERNEL);
 	if (!info) {
 	if (!info) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto out;
 		goto out;
 	}
 	}
 
 
-	memset(info, 0, size);
-
 	if (plat->init) {
 	if (plat->init) {
 		ret = plat->init();
 		ret = plat->init();
 		if (ret)
 		if (ret)

+ 2 - 6
drivers/mtd/maps/tqm834x.c

@@ -132,20 +132,16 @@ static int __init init_tqm834x_mtd(void)
 
 
 		pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx);
 		pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx);
 
 
-		map_banks[idx] =
-			(struct map_info *)kmalloc(sizeof(struct map_info),
-						   GFP_KERNEL);
+		map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL);
 		if (map_banks[idx] == NULL) {
 		if (map_banks[idx] == NULL) {
 			ret = -ENOMEM;
 			ret = -ENOMEM;
 			goto error_mem;
 			goto error_mem;
 		}
 		}
-		memset((void *)map_banks[idx], 0, sizeof(struct map_info));
-		map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL);
+		map_banks[idx]->name = kzalloc(16, GFP_KERNEL);
 		if (map_banks[idx]->name == NULL) {
 		if (map_banks[idx]->name == NULL) {
 			ret = -ENOMEM;
 			ret = -ENOMEM;
 			goto error_mem;
 			goto error_mem;
 		}
 		}
-		memset((void *)map_banks[idx]->name, 0, 16);
 
 
 		sprintf(map_banks[idx]->name, "TQM834x-%d", idx);
 		sprintf(map_banks[idx]->name, "TQM834x-%d", idx);
 		map_banks[idx]->size = flash_size;
 		map_banks[idx]->size = flash_size;

+ 1 - 2
drivers/mtd/maps/tqm8xxl.c

@@ -134,14 +134,13 @@ int __init init_tqm_mtd(void)
 
 
 		printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx);
 		printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx);
 
 
-		map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL);
+		map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL);
 		if(map_banks[idx] == NULL) {
 		if(map_banks[idx] == NULL) {
 			ret = -ENOMEM;
 			ret = -ENOMEM;
 			/* FIXME: What if some MTD devices were probed already? */
 			/* FIXME: What if some MTD devices were probed already? */
 			goto error_mem;
 			goto error_mem;
 		}
 		}
 
 
-		memset((void *)map_banks[idx], 0, sizeof(struct map_info));
 		map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL);
 		map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL);
 
 
 		if (!map_banks[idx]->name) {
 		if (!map_banks[idx]->name) {

+ 10 - 9
drivers/mtd/mtd_blkdevs.c

@@ -42,19 +42,20 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
 	unsigned long block, nsect;
 	unsigned long block, nsect;
 	char *buf;
 	char *buf;
 
 
-	block = req->sector;
-	nsect = req->current_nr_sectors;
+	block = req->sector << 9 >> tr->blkshift;
+	nsect = req->current_nr_sectors << 9 >> tr->blkshift;
+
 	buf = req->buffer;
 	buf = req->buffer;
 
 
 	if (!blk_fs_request(req))
 	if (!blk_fs_request(req))
 		return 0;
 		return 0;
 
 
-	if (block + nsect > get_capacity(req->rq_disk))
+	if (req->sector + req->current_nr_sectors > get_capacity(req->rq_disk))
 		return 0;
 		return 0;
 
 
 	switch(rq_data_dir(req)) {
 	switch(rq_data_dir(req)) {
 	case READ:
 	case READ:
-		for (; nsect > 0; nsect--, block++, buf += 512)
+		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
 			if (tr->readsect(dev, block, buf))
 			if (tr->readsect(dev, block, buf))
 				return 0;
 				return 0;
 		return 1;
 		return 1;
@@ -63,7 +64,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
 		if (!tr->writesect)
 		if (!tr->writesect)
 			return 0;
 			return 0;
 
 
-		for (; nsect > 0; nsect--, block++, buf += 512)
+		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
 			if (tr->writesect(dev, block, buf))
 			if (tr->writesect(dev, block, buf))
 				return 0;
 				return 0;
 		return 1;
 		return 1;
@@ -297,7 +298,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
 
 
 	/* 2.5 has capacity in units of 512 bytes while still
 	/* 2.5 has capacity in units of 512 bytes while still
 	   having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
 	   having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
-	set_capacity(gd, (new->size * new->blksize) >> 9);
+	set_capacity(gd, (new->size * tr->blksize) >> 9);
 
 
 	gd->private_data = new;
 	gd->private_data = new;
 	new->blkcore_priv = gd;
 	new->blkcore_priv = gd;
@@ -372,12 +373,10 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
 	if (!blktrans_notifier.list.next)
 	if (!blktrans_notifier.list.next)
 		register_mtd_user(&blktrans_notifier);
 		register_mtd_user(&blktrans_notifier);
 
 
-	tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
+	tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
 	if (!tr->blkcore_priv)
 	if (!tr->blkcore_priv)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));
-
 	mutex_lock(&mtd_table_mutex);
 	mutex_lock(&mtd_table_mutex);
 
 
 	ret = register_blkdev(tr->major, tr->name);
 	ret = register_blkdev(tr->major, tr->name);
@@ -401,6 +400,8 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
 	}
 	}
 
 
 	tr->blkcore_priv->rq->queuedata = tr;
 	tr->blkcore_priv->rq->queuedata = tr;
+	blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
+	tr->blkshift = ffs(tr->blksize) - 1;
 
 
 	ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL);
 	ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL);
 	if (ret < 0) {
 	if (ret < 0) {

+ 4 - 6
drivers/mtd/mtdblock.c

@@ -278,11 +278,10 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
 	}
 	}
 
 
 	/* OK, it's not open. Create cache info for it */
 	/* OK, it's not open. Create cache info for it */
-	mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
+	mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
 	if (!mtdblk)
 	if (!mtdblk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(mtdblk, 0, sizeof(*mtdblk));
 	mtdblk->count = 1;
 	mtdblk->count = 1;
 	mtdblk->mtd = mtd;
 	mtdblk->mtd = mtd;
 
 
@@ -339,16 +338,14 @@ static int mtdblock_flush(struct mtd_blktrans_dev *dev)
 
 
 static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
 {
-	struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 
 
 	if (!dev)
 	if (!dev)
 		return;
 		return;
 
 
-	memset(dev, 0, sizeof(*dev));
-
 	dev->mtd = mtd;
 	dev->mtd = mtd;
 	dev->devnum = mtd->index;
 	dev->devnum = mtd->index;
-	dev->blksize = 512;
+
 	dev->size = mtd->size >> 9;
 	dev->size = mtd->size >> 9;
 	dev->tr = tr;
 	dev->tr = tr;
 
 
@@ -368,6 +365,7 @@ static struct mtd_blktrans_ops mtdblock_tr = {
 	.name		= "mtdblock",
 	.name		= "mtdblock",
 	.major		= 31,
 	.major		= 31,
 	.part_bits	= 0,
 	.part_bits	= 0,
+	.blksize 	= 512,
 	.open		= mtdblock_open,
 	.open		= mtdblock_open,
 	.flush		= mtdblock_flush,
 	.flush		= mtdblock_flush,
 	.release	= mtdblock_release,
 	.release	= mtdblock_release,

+ 3 - 4
drivers/mtd/mtdblock_ro.c

@@ -33,16 +33,14 @@ static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
 
 
 static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
 {
-	struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 
 
 	if (!dev)
 	if (!dev)
 		return;
 		return;
 
 
-	memset(dev, 0, sizeof(*dev));
-
 	dev->mtd = mtd;
 	dev->mtd = mtd;
 	dev->devnum = mtd->index;
 	dev->devnum = mtd->index;
-	dev->blksize = 512;
+
 	dev->size = mtd->size >> 9;
 	dev->size = mtd->size >> 9;
 	dev->tr = tr;
 	dev->tr = tr;
 	dev->readonly = 1;
 	dev->readonly = 1;
@@ -60,6 +58,7 @@ static struct mtd_blktrans_ops mtdblock_tr = {
 	.name		= "mtdblock",
 	.name		= "mtdblock",
 	.major		= 31,
 	.major		= 31,
 	.part_bits	= 0,
 	.part_bits	= 0,
+	.blksize 	= 512,
 	.readsect	= mtdblock_readsect,
 	.readsect	= mtdblock_readsect,
 	.writesect	= mtdblock_writesect,
 	.writesect	= mtdblock_writesect,
 	.add_mtd	= mtdblock_add_mtd,
 	.add_mtd	= mtdblock_add_mtd,

+ 11 - 12
drivers/mtd/mtdchar.c

@@ -7,6 +7,7 @@
 
 
 #include <linux/device.h>
 #include <linux/device.h>
 #include <linux/fs.h>
 #include <linux/fs.h>
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/module.h>
@@ -100,8 +101,8 @@ static int mtd_open(struct inode *inode, struct file *file)
 
 
 	mtd = get_mtd_device(NULL, devnum);
 	mtd = get_mtd_device(NULL, devnum);
 
 
-	if (!mtd)
-		return -ENODEV;
+	if (IS_ERR(mtd))
+		return PTR_ERR(mtd);
 
 
 	if (MTD_ABSENT == mtd->type) {
 	if (MTD_ABSENT == mtd->type) {
 		put_mtd_device(mtd);
 		put_mtd_device(mtd);
@@ -431,7 +432,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		if(!(file->f_mode & 2))
 		if(!(file->f_mode & 2))
 			return -EPERM;
 			return -EPERM;
 
 
-		erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
+		erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL);
 		if (!erase)
 		if (!erase)
 			ret = -ENOMEM;
 			ret = -ENOMEM;
 		else {
 		else {
@@ -440,7 +441,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
 
 			init_waitqueue_head(&waitq);
 			init_waitqueue_head(&waitq);
 
 
-			memset (erase,0,sizeof(struct erase_info));
 			if (copy_from_user(&erase->addr, argp,
 			if (copy_from_user(&erase->addr, argp,
 				    sizeof(struct erase_info_user))) {
 				    sizeof(struct erase_info_user))) {
 				kfree(erase);
 				kfree(erase);
@@ -499,13 +499,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 
 
-		ops.len = buf.length;
 		ops.ooblen = buf.length;
 		ops.ooblen = buf.length;
 		ops.ooboffs = buf.start & (mtd->oobsize - 1);
 		ops.ooboffs = buf.start & (mtd->oobsize - 1);
 		ops.datbuf = NULL;
 		ops.datbuf = NULL;
 		ops.mode = MTD_OOB_PLACE;
 		ops.mode = MTD_OOB_PLACE;
 
 
-		if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
+		if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
 			return -EINVAL;
 			return -EINVAL;
 
 
 		ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
 		ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
@@ -520,7 +519,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		buf.start &= ~(mtd->oobsize - 1);
 		buf.start &= ~(mtd->oobsize - 1);
 		ret = mtd->write_oob(mtd, buf.start, &ops);
 		ret = mtd->write_oob(mtd, buf.start, &ops);
 
 
-		if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen,
+		if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen,
 				 sizeof(uint32_t)))
 				 sizeof(uint32_t)))
 			ret = -EFAULT;
 			ret = -EFAULT;
 
 
@@ -548,7 +547,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 
 
-		ops.len = buf.length;
 		ops.ooblen = buf.length;
 		ops.ooblen = buf.length;
 		ops.ooboffs = buf.start & (mtd->oobsize - 1);
 		ops.ooboffs = buf.start & (mtd->oobsize - 1);
 		ops.datbuf = NULL;
 		ops.datbuf = NULL;
@@ -564,10 +562,10 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		buf.start &= ~(mtd->oobsize - 1);
 		buf.start &= ~(mtd->oobsize - 1);
 		ret = mtd->read_oob(mtd, buf.start, &ops);
 		ret = mtd->read_oob(mtd, buf.start, &ops);
 
 
-		if (put_user(ops.retlen, (uint32_t __user *)argp))
+		if (put_user(ops.oobretlen, (uint32_t __user *)argp))
 			ret = -EFAULT;
 			ret = -EFAULT;
-		else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf,
-						    ops.retlen))
+		else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf,
+						    ops.oobretlen))
 			ret = -EFAULT;
 			ret = -EFAULT;
 
 
 		kfree(ops.oobbuf);
 		kfree(ops.oobbuf);
@@ -616,6 +614,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
 		memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
 		memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
 		memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
 		       sizeof(oi.oobfree));
 		       sizeof(oi.oobfree));
+		oi.eccbytes = mtd->ecclayout->eccbytes;
 
 
 		if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
 		if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
 			return -EFAULT;
 			return -EFAULT;
@@ -715,7 +714,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		if (!mtd->ecclayout)
 		if (!mtd->ecclayout)
 			return -EOPNOTSUPP;
 			return -EOPNOTSUPP;
 
 
-		if (copy_to_user(argp, &mtd->ecclayout,
+		if (copy_to_user(argp, mtd->ecclayout,
 				 sizeof(struct nand_ecclayout)))
 				 sizeof(struct nand_ecclayout)))
 			return -EFAULT;
 			return -EFAULT;
 		break;
 		break;

+ 26 - 17
drivers/mtd/mtdconcat.c

@@ -247,7 +247,7 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
 	struct mtd_oob_ops devops = *ops;
 	struct mtd_oob_ops devops = *ops;
 	int i, err, ret = 0;
 	int i, err, ret = 0;
 
 
-	ops->retlen = 0;
+	ops->retlen = ops->oobretlen = 0;
 
 
 	for (i = 0; i < concat->num_subdev; i++) {
 	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		struct mtd_info *subdev = concat->subdev[i];
@@ -263,6 +263,7 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
 
 
 		err = subdev->read_oob(subdev, from, &devops);
 		err = subdev->read_oob(subdev, from, &devops);
 		ops->retlen += devops.retlen;
 		ops->retlen += devops.retlen;
+		ops->oobretlen += devops.oobretlen;
 
 
 		/* Save information about bitflips! */
 		/* Save information about bitflips! */
 		if (unlikely(err)) {
 		if (unlikely(err)) {
@@ -278,14 +279,18 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
 				return err;
 				return err;
 		}
 		}
 
 
-		devops.len = ops->len - ops->retlen;
-		if (!devops.len)
-			return ret;
-
-		if (devops.datbuf)
+		if (devops.datbuf) {
+			devops.len = ops->len - ops->retlen;
+			if (!devops.len)
+				return ret;
 			devops.datbuf += devops.retlen;
 			devops.datbuf += devops.retlen;
-		if (devops.oobbuf)
-			devops.oobbuf += devops.ooblen;
+		}
+		if (devops.oobbuf) {
+			devops.ooblen = ops->ooblen - ops->oobretlen;
+			if (!devops.ooblen)
+				return ret;
+			devops.oobbuf += ops->oobretlen;
+		}
 
 
 		from = 0;
 		from = 0;
 	}
 	}
@@ -321,14 +326,18 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
 		if (err)
 		if (err)
 			return err;
 			return err;
 
 
-		devops.len = ops->len - ops->retlen;
-		if (!devops.len)
-			return 0;
-
-		if (devops.datbuf)
+		if (devops.datbuf) {
+			devops.len = ops->len - ops->retlen;
+			if (!devops.len)
+				return 0;
 			devops.datbuf += devops.retlen;
 			devops.datbuf += devops.retlen;
-		if (devops.oobbuf)
-			devops.oobbuf += devops.ooblen;
+		}
+		if (devops.oobbuf) {
+			devops.ooblen = ops->ooblen - ops->oobretlen;
+			if (!devops.ooblen)
+				return 0;
+			devops.oobbuf += devops.oobretlen;
+		}
 		to = 0;
 		to = 0;
 	}
 	}
 	return -EINVAL;
 	return -EINVAL;
@@ -699,14 +708,13 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to c
 
 
 	/* allocate the device structure */
 	/* allocate the device structure */
 	size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
 	size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
-	concat = kmalloc(size, GFP_KERNEL);
+	concat = kzalloc(size, GFP_KERNEL);
 	if (!concat) {
 	if (!concat) {
 		printk
 		printk
 		    ("memory allocation error while creating concatenated device \"%s\"\n",
 		    ("memory allocation error while creating concatenated device \"%s\"\n",
 		     name);
 		     name);
 		return NULL;
 		return NULL;
 	}
 	}
-	memset(concat, 0, size);
 	concat->subdev = (struct mtd_info **) (concat + 1);
 	concat->subdev = (struct mtd_info **) (concat + 1);
 
 
 	/*
 	/*
@@ -764,6 +772,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to c
 		concat->mtd.ecc_stats.badblocks +=
 		concat->mtd.ecc_stats.badblocks +=
 			subdev[i]->ecc_stats.badblocks;
 			subdev[i]->ecc_stats.badblocks;
 		if (concat->mtd.writesize   !=  subdev[i]->writesize ||
 		if (concat->mtd.writesize   !=  subdev[i]->writesize ||
+		    concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
 		    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
 		    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
 		    concat->mtd.ecctype    !=  subdev[i]->ecctype ||
 		    concat->mtd.ecctype    !=  subdev[i]->ecctype ||
 		    concat->mtd.eccsize    !=  subdev[i]->eccsize ||
 		    concat->mtd.eccsize    !=  subdev[i]->eccsize ||

+ 78 - 15
drivers/mtd/mtdcore.c

@@ -15,6 +15,7 @@
 #include <linux/timer.h>
 #include <linux/timer.h>
 #include <linux/major.h>
 #include <linux/major.h>
 #include <linux/fs.h>
 #include <linux/fs.h>
+#include <linux/err.h>
 #include <linux/ioctl.h>
 #include <linux/ioctl.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/compatmac.h>
@@ -192,14 +193,14 @@ int unregister_mtd_user (struct mtd_notifier *old)
  *	Given a number and NULL address, return the num'th entry in the device
  *	Given a number and NULL address, return the num'th entry in the device
  *	table, if any.	Given an address and num == -1, search the device table
  *	table, if any.	Given an address and num == -1, search the device table
  *	for a device with that address and return if it's still present. Given
  *	for a device with that address and return if it's still present. Given
- *	both, return the num'th driver only if its address matches. Return NULL
- *	if not.
+ *	both, return the num'th driver only if its address matches. Return
+ *	error code if not.
  */
  */
 
 
 struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
 struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
 {
 {
 	struct mtd_info *ret = NULL;
 	struct mtd_info *ret = NULL;
-	int i;
+	int i, err = -ENODEV;
 
 
 	mutex_lock(&mtd_table_mutex);
 	mutex_lock(&mtd_table_mutex);
 
 
@@ -213,14 +214,73 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
 			ret = NULL;
 			ret = NULL;
 	}
 	}
 
 
-	if (ret && !try_module_get(ret->owner))
-		ret = NULL;
+	if (!ret)
+		goto out_unlock;
+
+	if (!try_module_get(ret->owner))
+		goto out_unlock;
 
 
-	if (ret)
-		ret->usecount++;
+	if (ret->get_device) {
+		err = ret->get_device(ret);
+		if (err)
+			goto out_put;
+	}
 
 
+	ret->usecount++;
 	mutex_unlock(&mtd_table_mutex);
 	mutex_unlock(&mtd_table_mutex);
 	return ret;
 	return ret;
+
+out_put:
+	module_put(ret->owner);
+out_unlock:
+	mutex_unlock(&mtd_table_mutex);
+	return ERR_PTR(err);
+}
+
+/**
+ *	get_mtd_device_nm - obtain a validated handle for an MTD device by
+ *	device name
+ *	@name: MTD device name to open
+ *
+ * 	This function returns MTD device description structure in case of
+ * 	success and an error code in case of failure.
+ */
+
+struct mtd_info *get_mtd_device_nm(const char *name)
+{
+	int i, err = -ENODEV;
+	struct mtd_info *mtd = NULL;
+
+	mutex_lock(&mtd_table_mutex);
+
+	for (i = 0; i < MAX_MTD_DEVICES; i++) {
+		if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) {
+			mtd = mtd_table[i];
+			break;
+		}
+	}
+
+	if (!mtd)
+		goto out_unlock;
+
+	if (!try_module_get(mtd->owner))
+		goto out_unlock;
+
+	if (mtd->get_device) {
+		err = mtd->get_device(mtd);
+		if (err)
+			goto out_put;
+	}
+
+	mtd->usecount++;
+	mutex_unlock(&mtd_table_mutex);
+	return mtd;
+
+out_put:
+	module_put(mtd->owner);
+out_unlock:
+	mutex_unlock(&mtd_table_mutex);
+	return ERR_PTR(err);
 }
 }
 
 
 void put_mtd_device(struct mtd_info *mtd)
 void put_mtd_device(struct mtd_info *mtd)
@@ -229,6 +289,8 @@ void put_mtd_device(struct mtd_info *mtd)
 
 
 	mutex_lock(&mtd_table_mutex);
 	mutex_lock(&mtd_table_mutex);
 	c = --mtd->usecount;
 	c = --mtd->usecount;
+	if (mtd->put_device)
+		mtd->put_device(mtd);
 	mutex_unlock(&mtd_table_mutex);
 	mutex_unlock(&mtd_table_mutex);
 	BUG_ON(c < 0);
 	BUG_ON(c < 0);
 
 
@@ -236,7 +298,7 @@ void put_mtd_device(struct mtd_info *mtd)
 }
 }
 
 
 /* default_mtd_writev - default mtd writev method for MTD devices that
 /* default_mtd_writev - default mtd writev method for MTD devices that
- *			dont implement their own
+ *			don't implement their own
  */
  */
 
 
 int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
 int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
@@ -264,13 +326,14 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
 	return ret;
 	return ret;
 }
 }
 
 
-EXPORT_SYMBOL(add_mtd_device);
-EXPORT_SYMBOL(del_mtd_device);
-EXPORT_SYMBOL(get_mtd_device);
-EXPORT_SYMBOL(put_mtd_device);
-EXPORT_SYMBOL(register_mtd_user);
-EXPORT_SYMBOL(unregister_mtd_user);
-EXPORT_SYMBOL(default_mtd_writev);
+EXPORT_SYMBOL_GPL(add_mtd_device);
+EXPORT_SYMBOL_GPL(del_mtd_device);
+EXPORT_SYMBOL_GPL(get_mtd_device);
+EXPORT_SYMBOL_GPL(get_mtd_device_nm);
+EXPORT_SYMBOL_GPL(put_mtd_device);
+EXPORT_SYMBOL_GPL(register_mtd_user);
+EXPORT_SYMBOL_GPL(unregister_mtd_user);
+EXPORT_SYMBOL_GPL(default_mtd_writev);
 
 
 #ifdef CONFIG_PROC_FS
 #ifdef CONFIG_PROC_FS
 
 

+ 4 - 4
drivers/mtd/mtdpart.c

@@ -94,7 +94,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
 
 
 	if (from >= mtd->size)
 	if (from >= mtd->size)
 		return -EINVAL;
 		return -EINVAL;
-	if (from + ops->len > mtd->size)
+	if (ops->datbuf && from + ops->len > mtd->size)
 		return -EINVAL;
 		return -EINVAL;
 	res = part->master->read_oob(part->master, from + part->offset, ops);
 	res = part->master->read_oob(part->master, from + part->offset, ops);
 
 
@@ -161,7 +161,7 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
 
 
 	if (to >= mtd->size)
 	if (to >= mtd->size)
 		return -EINVAL;
 		return -EINVAL;
-	if (to + ops->len > mtd->size)
+	if (ops->datbuf && to + ops->len > mtd->size)
 		return -EINVAL;
 		return -EINVAL;
 	return part->master->write_oob(part->master, to + part->offset, ops);
 	return part->master->write_oob(part->master, to + part->offset, ops);
 }
 }
@@ -323,14 +323,13 @@ int add_mtd_partitions(struct mtd_info *master,
 	for (i = 0; i < nbparts; i++) {
 	for (i = 0; i < nbparts; i++) {
 
 
 		/* allocate the partition structure */
 		/* allocate the partition structure */
-		slave = kmalloc (sizeof(*slave), GFP_KERNEL);
+		slave = kzalloc (sizeof(*slave), GFP_KERNEL);
 		if (!slave) {
 		if (!slave) {
 			printk ("memory allocation error while creating partitions for \"%s\"\n",
 			printk ("memory allocation error while creating partitions for \"%s\"\n",
 				master->name);
 				master->name);
 			del_mtd_partitions(master);
 			del_mtd_partitions(master);
 			return -ENOMEM;
 			return -ENOMEM;
 		}
 		}
-		memset(slave, 0, sizeof(*slave));
 		list_add(&slave->list, &mtd_partitions);
 		list_add(&slave->list, &mtd_partitions);
 
 
 		/* set up the MTD object for this partition */
 		/* set up the MTD object for this partition */
@@ -341,6 +340,7 @@ int add_mtd_partitions(struct mtd_info *master,
 		slave->mtd.oobsize = master->oobsize;
 		slave->mtd.oobsize = master->oobsize;
 		slave->mtd.ecctype = master->ecctype;
 		slave->mtd.ecctype = master->ecctype;
 		slave->mtd.eccsize = master->eccsize;
 		slave->mtd.eccsize = master->eccsize;
+		slave->mtd.subpage_sft = master->subpage_sft;
 
 
 		slave->mtd.name = parts[i].name;
 		slave->mtd.name = parts[i].name;
 		slave->mtd.bank_size = master->bank_size;
 		slave->mtd.bank_size = master->bank_size;

+ 16 - 0
drivers/mtd/nand/Kconfig

@@ -90,6 +90,7 @@ config MTD_NAND_RTC_FROM4
 	depends on MTD_NAND && SH_SOLUTION_ENGINE
 	depends on MTD_NAND && SH_SOLUTION_ENGINE
 	select REED_SOLOMON
 	select REED_SOLOMON
 	select REED_SOLOMON_DEC8
 	select REED_SOLOMON_DEC8
+	select BITREVERSE
 	help
 	help
 	  This enables the driver for the Renesas Technology AG-AND
 	  This enables the driver for the Renesas Technology AG-AND
 	  flash interface board (FROM_BOARD4)
 	  flash interface board (FROM_BOARD4)
@@ -132,6 +133,7 @@ config MTD_NAND_S3C2410_HWECC
 config MTD_NAND_NDFC
 config MTD_NAND_NDFC
 	tristate "NDFC NanD Flash Controller"
 	tristate "NDFC NanD Flash Controller"
 	depends on MTD_NAND && 44x
 	depends on MTD_NAND && 44x
+	select MTD_NAND_ECC_SMC
 	help
 	help
 	 NDFC Nand Flash Controllers are integrated in EP44x SoCs
 	 NDFC Nand Flash Controllers are integrated in EP44x SoCs
 
 
@@ -219,6 +221,13 @@ config MTD_NAND_SHARPSL
 	tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
 	tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
 	depends on MTD_NAND && ARCH_PXA
 	depends on MTD_NAND && ARCH_PXA
 
 
+config MTD_NAND_CAFE
+       tristate "NAND support for OLPC CAFÉ chip"
+       depends on PCI
+       help
+	 Use NAND flash attached to the CAFÉ chip designed for the $100
+	 laptop.
+
 config MTD_NAND_CS553X
 config MTD_NAND_CS553X
 	tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
 	tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
 	depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH)
 	depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH)
@@ -232,6 +241,13 @@ config MTD_NAND_CS553X
 
 
 	  If you say "m", the module will be called "cs553x_nand.ko".
 	  If you say "m", the module will be called "cs553x_nand.ko".
 
 
+config MTD_NAND_AT91
+	bool "Support for NAND Flash / SmartMedia on AT91"
+	depends on MTD_NAND && ARCH_AT91
+	help
+	  Enables support for NAND Flash / Smart Media Card interface
+	  on Atmel AT91 processors.
+
 config MTD_NAND_NANDSIM
 config MTD_NAND_NANDSIM
 	tristate "Support for NAND Flash Simulator"
 	tristate "Support for NAND Flash Simulator"
 	depends on MTD_NAND && MTD_PARTITIONS
 	depends on MTD_NAND && MTD_PARTITIONS

+ 4 - 1
drivers/mtd/nand/Makefile

@@ -6,6 +6,7 @@
 obj-$(CONFIG_MTD_NAND)			+= nand.o nand_ecc.o
 obj-$(CONFIG_MTD_NAND)			+= nand.o nand_ecc.o
 obj-$(CONFIG_MTD_NAND_IDS)		+= nand_ids.o
 obj-$(CONFIG_MTD_NAND_IDS)		+= nand_ids.o
 
 
+obj-$(CONFIG_MTD_NAND_CAFE)		+= cafe_nand.o
 obj-$(CONFIG_MTD_NAND_SPIA)		+= spia.o
 obj-$(CONFIG_MTD_NAND_SPIA)		+= spia.o
 obj-$(CONFIG_MTD_NAND_AMS_DELTA)	+= ams-delta.o
 obj-$(CONFIG_MTD_NAND_AMS_DELTA)	+= ams-delta.o
 obj-$(CONFIG_MTD_NAND_TOTO)		+= toto.o
 obj-$(CONFIG_MTD_NAND_TOTO)		+= toto.o
@@ -22,5 +23,7 @@ obj-$(CONFIG_MTD_NAND_TS7250)		+= ts7250.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)		+= nandsim.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)		+= nandsim.o
 obj-$(CONFIG_MTD_NAND_CS553X)		+= cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_CS553X)		+= cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)		+= ndfc.o
 obj-$(CONFIG_MTD_NAND_NDFC)		+= ndfc.o
+obj-$(CONFIG_MTD_NAND_AT91)		+= at91_nand.o
 
 
-nand-objs = nand_base.o nand_bbt.o
+nand-objs := nand_base.o nand_bbt.o
+cafe_nand-objs := cafe.o cafe_ecc.o

+ 223 - 0
drivers/mtd/nand/at91_nand.c

@@ -0,0 +1,223 @@
+/*
+ * drivers/mtd/nand/at91_nand.c
+ *
+ *  Copyright (C) 2003 Rick Bronson
+ *
+ *  Derived from drivers/mtd/nand/autcpu12.c
+ *	 Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ *  Derived from drivers/mtd/spia.c
+ *	 Copyright (C) 2000 Steven J. Hill (sjhill@cotw.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/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/sizes.h>
+
+#include <asm/hardware.h>
+#include <asm/arch/board.h>
+#include <asm/arch/gpio.h>
+
+struct at91_nand_host {
+	struct nand_chip	nand_chip;
+	struct mtd_info		mtd;
+	void __iomem		*io_base;
+	struct at91_nand_data	*board;
+};
+
+/*
+ * Hardware specific access to control-lines
+ */
+static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+	struct nand_chip *nand_chip = mtd->priv;
+	struct at91_nand_host *host = nand_chip->priv;
+
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	if (ctrl & NAND_CLE)
+		writeb(cmd, host->io_base + (1 << host->board->cle));
+	else
+		writeb(cmd, host->io_base + (1 << host->board->ale));
+}
+
+/*
+ * Read the Device Ready pin.
+ */
+static int at91_nand_device_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *nand_chip = mtd->priv;
+	struct at91_nand_host *host = nand_chip->priv;
+
+	return at91_get_gpio_value(host->board->rdy_pin);
+}
+
+/*
+ * Enable NAND.
+ */
+static void at91_nand_enable(struct at91_nand_host *host)
+{
+	if (host->board->enable_pin)
+		at91_set_gpio_value(host->board->enable_pin, 0);
+}
+
+/*
+ * Disable NAND.
+ */
+static void at91_nand_disable(struct at91_nand_host *host)
+{
+	if (host->board->enable_pin)
+		at91_set_gpio_value(host->board->enable_pin, 1);
+}
+
+/*
+ * Probe for the NAND device.
+ */
+static int __init at91_nand_probe(struct platform_device *pdev)
+{
+	struct at91_nand_host *host;
+	struct mtd_info *mtd;
+	struct nand_chip *nand_chip;
+	int res;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	struct mtd_partition *partitions = NULL;
+	int num_partitions = 0;
+#endif
+
+	/* Allocate memory for the device structure (and zero it) */
+	host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL);
+	if (!host) {
+		printk(KERN_ERR "at91_nand: failed to allocate device structure.\n");
+		return -ENOMEM;
+	}
+
+	host->io_base = ioremap(pdev->resource[0].start,
+				pdev->resource[0].end - pdev->resource[0].start + 1);
+	if (host->io_base == NULL) {
+		printk(KERN_ERR "at91_nand: ioremap failed\n");
+		kfree(host);
+		return -EIO;
+	}
+
+	mtd = &host->mtd;
+	nand_chip = &host->nand_chip;
+	host->board = pdev->dev.platform_data;
+
+	nand_chip->priv = host;		/* link the private data structures */
+	mtd->priv = nand_chip;
+	mtd->owner = THIS_MODULE;
+
+	/* Set address of NAND IO lines */
+	nand_chip->IO_ADDR_R = host->io_base;
+	nand_chip->IO_ADDR_W = host->io_base;
+	nand_chip->cmd_ctrl = at91_nand_cmd_ctrl;
+	nand_chip->dev_ready = at91_nand_device_ready;
+	nand_chip->ecc.mode = NAND_ECC_SOFT;	/* enable ECC */
+	nand_chip->chip_delay = 20;		/* 20us command delay time */
+
+	if (host->board->bus_width_16)		/* 16-bit bus width */
+		nand_chip->options |= NAND_BUSWIDTH_16;
+
+	platform_set_drvdata(pdev, host);
+	at91_nand_enable(host);
+
+	if (host->board->det_pin) {
+		if (at91_get_gpio_value(host->board->det_pin)) {
+			printk ("No SmartMedia card inserted.\n");
+			res = ENXIO;
+			goto out;
+		}
+	}
+
+	/* Scan to find existance of the device */
+	if (nand_scan(mtd, 1)) {
+		res = -ENXIO;
+		goto out;
+	}
+
+#ifdef CONFIG_MTD_PARTITIONS
+	if (host->board->partition_info)
+		partitions = host->board->partition_info(mtd->size, &num_partitions);
+
+	if ((!partitions) || (num_partitions == 0)) {
+		printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n");
+		res = ENXIO;
+		goto release;
+	}
+
+	res = add_mtd_partitions(mtd, partitions, num_partitions);
+#else
+	res = add_mtd_device(mtd);
+#endif
+
+	if (!res)
+		return res;
+
+release:
+	nand_release(mtd);
+out:
+	at91_nand_disable(host);
+	platform_set_drvdata(pdev, NULL);
+	iounmap(host->io_base);
+	kfree(host);
+	return res;
+}
+
+/*
+ * Remove a NAND device.
+ */
+static int __devexit at91_nand_remove(struct platform_device *pdev)
+{
+	struct at91_nand_host *host = platform_get_drvdata(pdev);
+	struct mtd_info *mtd = &host->mtd;
+
+	nand_release(mtd);
+
+	at91_nand_disable(host);
+
+	iounmap(host->io_base);
+	kfree(host);
+
+	return 0;
+}
+
+static struct platform_driver at91_nand_driver = {
+	.probe		= at91_nand_probe,
+	.remove		= at91_nand_remove,
+	.driver		= {
+		.name	= "at91_nand",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init at91_nand_init(void)
+{
+	return platform_driver_register(&at91_nand_driver);
+}
+
+
+static void __exit at91_nand_exit(void)
+{
+	platform_driver_unregister(&at91_nand_driver);
+}
+
+
+module_init(at91_nand_init);
+module_exit(at91_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rick Bronson");
+MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200");

+ 770 - 0
drivers/mtd/nand/cafe.c

@@ -0,0 +1,770 @@
+/*
+ * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01
+ *
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
+ */
+
+#define DEBUG
+
+#include <linux/device.h>
+#undef DEBUG
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#define CAFE_NAND_CTRL1		0x00
+#define CAFE_NAND_CTRL2		0x04
+#define CAFE_NAND_CTRL3		0x08
+#define CAFE_NAND_STATUS	0x0c
+#define CAFE_NAND_IRQ		0x10
+#define CAFE_NAND_IRQ_MASK	0x14
+#define CAFE_NAND_DATA_LEN	0x18
+#define CAFE_NAND_ADDR1		0x1c
+#define CAFE_NAND_ADDR2		0x20
+#define CAFE_NAND_TIMING1	0x24
+#define CAFE_NAND_TIMING2	0x28
+#define CAFE_NAND_TIMING3	0x2c
+#define CAFE_NAND_NONMEM	0x30
+#define CAFE_NAND_ECC_RESULT	0x3C
+#define CAFE_NAND_DMA_CTRL	0x40
+#define CAFE_NAND_DMA_ADDR0	0x44
+#define CAFE_NAND_DMA_ADDR1	0x48
+#define CAFE_NAND_ECC_SYN01	0x50
+#define CAFE_NAND_ECC_SYN23	0x54
+#define CAFE_NAND_ECC_SYN45	0x58
+#define CAFE_NAND_ECC_SYN67	0x5c
+#define CAFE_NAND_READ_DATA	0x1000
+#define CAFE_NAND_WRITE_DATA	0x2000
+
+#define CAFE_GLOBAL_CTRL	0x3004
+#define CAFE_GLOBAL_IRQ		0x3008
+#define CAFE_GLOBAL_IRQ_MASK	0x300c
+#define CAFE_NAND_RESET		0x3034
+
+int cafe_correct_ecc(unsigned char *buf,
+		     unsigned short *chk_syndrome_list);
+
+struct cafe_priv {
+	struct nand_chip nand;
+	struct pci_dev *pdev;
+	void __iomem *mmio;
+	uint32_t ctl1;
+	uint32_t ctl2;
+	int datalen;
+	int nr_data;
+	int data_pos;
+	int page_addr;
+	dma_addr_t dmaaddr;
+	unsigned char *dmabuf;
+};
+
+static int usedma = 1;
+module_param(usedma, int, 0644);
+
+static int skipbbt = 0;
+module_param(skipbbt, int, 0644);
+
+static int debug = 0;
+module_param(debug, int, 0644);
+
+static int regdebug = 0;
+module_param(regdebug, int, 0644);
+
+static int checkecc = 1;
+module_param(checkecc, int, 0644);
+
+static int slowtiming = 0;
+module_param(slowtiming, int, 0644);
+
+/* Hrm. Why isn't this already conditional on something in the struct device? */
+#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
+
+/* Make it easier to switch to PIO if we need to */
+#define cafe_readl(cafe, addr)			readl((cafe)->mmio + CAFE_##addr)
+#define cafe_writel(cafe, datum, addr)		writel(datum, (cafe)->mmio + CAFE_##addr)
+
+static int cafe_device_ready(struct mtd_info *mtd)
+{
+	struct cafe_priv *cafe = mtd->priv;
+	int result = !!(cafe_readl(cafe, NAND_STATUS) | 0x40000000);
+	uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
+
+	cafe_writel(cafe, irqs, NAND_IRQ);
+
+	cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n",
+		result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ),
+		cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK));
+
+	return result;
+}
+
+
+static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct cafe_priv *cafe = mtd->priv;
+
+	if (usedma)
+		memcpy(cafe->dmabuf + cafe->datalen, buf, len);
+	else
+		memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len);
+
+	cafe->datalen += len;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n",
+		len, cafe->datalen);
+}
+
+static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct cafe_priv *cafe = mtd->priv;
+
+	if (usedma)
+		memcpy(buf, cafe->dmabuf + cafe->datalen, len);
+	else
+		memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len);
+
+	cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n",
+		  len, cafe->datalen);
+	cafe->datalen += len;
+}
+
+static uint8_t cafe_read_byte(struct mtd_info *mtd)
+{
+	struct cafe_priv *cafe = mtd->priv;
+	uint8_t d;
+
+	cafe_read_buf(mtd, &d, 1);
+	cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d);
+
+	return d;
+}
+
+static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+			      int column, int page_addr)
+{
+	struct cafe_priv *cafe = mtd->priv;
+	int adrbytes = 0;
+	uint32_t ctl1;
+	uint32_t doneint = 0x80000000;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n",
+		command, column, page_addr);
+
+	if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) {
+		/* Second half of a command we already calculated */
+		cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2);
+		ctl1 = cafe->ctl1;
+		cafe->ctl2 &= ~(1<<30);
+		cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n",
+			  cafe->ctl1, cafe->nr_data);
+		goto do_command;
+	}
+	/* Reset ECC engine */
+	cafe_writel(cafe, 0, NAND_CTRL2);
+
+	/* Emulate NAND_CMD_READOOB on large-page chips */
+	if (mtd->writesize > 512 &&
+	    command == NAND_CMD_READOOB) {
+		column += mtd->writesize;
+		command = NAND_CMD_READ0;
+	}
+
+	/* FIXME: Do we need to send read command before sending data
+	   for small-page chips, to position the buffer correctly? */
+
+	if (column != -1) {
+		cafe_writel(cafe, column, NAND_ADDR1);
+		adrbytes = 2;
+		if (page_addr != -1)
+			goto write_adr2;
+	} else if (page_addr != -1) {
+		cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1);
+		page_addr >>= 16;
+	write_adr2:
+		cafe_writel(cafe, page_addr, NAND_ADDR2);
+		adrbytes += 2;
+		if (mtd->size > mtd->writesize << 16)
+			adrbytes++;
+	}
+
+	cafe->data_pos = cafe->datalen = 0;
+
+	/* Set command valid bit */
+	ctl1 = 0x80000000 | command;
+
+	/* Set RD or WR bits as appropriate */
+	if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) {
+		ctl1 |= (1<<26); /* rd */
+		/* Always 5 bytes, for now */
+		cafe->datalen = 4;
+		/* And one address cycle -- even for STATUS, since the controller doesn't work without */
+		adrbytes = 1;
+	} else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
+		   command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) {
+		ctl1 |= 1<<26; /* rd */
+		/* For now, assume just read to end of page */
+		cafe->datalen = mtd->writesize + mtd->oobsize - column;
+	} else if (command == NAND_CMD_SEQIN)
+		ctl1 |= 1<<25; /* wr */
+
+	/* Set number of address bytes */
+	if (adrbytes)
+		ctl1 |= ((adrbytes-1)|8) << 27;
+
+	if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) {
+		/* Ignore the first command of a pair; the hardware
+		   deals with them both at once, later */
+		cafe->ctl1 = ctl1;
+		cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n",
+			  cafe->ctl1, cafe->datalen);
+		return;
+	}
+	/* RNDOUT and READ0 commands need a following byte */
+	if (command == NAND_CMD_RNDOUT)
+		cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2);
+	else if (command == NAND_CMD_READ0 && mtd->writesize > 512)
+		cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2);
+
+ do_command:
+	cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n",
+		cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2));
+
+	/* NB: The datasheet lies -- we really should be subtracting 1 here */
+	cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN);
+	cafe_writel(cafe, 0x90000000, NAND_IRQ);
+	if (usedma && (ctl1 & (3<<25))) {
+		uint32_t dmactl = 0xc0000000 + cafe->datalen;
+		/* If WR or RD bits set, set up DMA */
+		if (ctl1 & (1<<26)) {
+			/* It's a read */
+			dmactl |= (1<<29);
+			/* ... so it's done when the DMA is done, not just
+			   the command. */
+			doneint = 0x10000000;
+		}
+		cafe_writel(cafe, dmactl, NAND_DMA_CTRL);
+	}
+	cafe->datalen = 0;
+
+	if (unlikely(regdebug)) {
+		int i;
+		printk("About to write command %08x to register 0\n", ctl1);
+		for (i=4; i< 0x5c; i+=4)
+			printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
+	}
+
+	cafe_writel(cafe, ctl1, NAND_CTRL1);
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay(100);
+
+	if (1) {
+		int c = 500000;
+		uint32_t irqs;
+
+		while (c--) {
+			irqs = cafe_readl(cafe, NAND_IRQ);
+			if (irqs & doneint)
+				break;
+			udelay(1);
+			if (!(c % 100000))
+				cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs);
+			cpu_relax();
+		}
+		cafe_writel(cafe, doneint, NAND_IRQ);
+		cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n",
+			     command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ));
+	}
+
+	WARN_ON(cafe->ctl2 & (1<<30));
+
+	switch (command) {
+
+	case NAND_CMD_CACHEDPROG:
+	case NAND_CMD_PAGEPROG:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_RNDIN:
+	case NAND_CMD_STATUS:
+	case NAND_CMD_DEPLETE1:
+	case NAND_CMD_RNDOUT:
+	case NAND_CMD_STATUS_ERROR:
+	case NAND_CMD_STATUS_ERROR0:
+	case NAND_CMD_STATUS_ERROR1:
+	case NAND_CMD_STATUS_ERROR2:
+	case NAND_CMD_STATUS_ERROR3:
+		cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
+		return;
+	}
+	nand_wait_ready(mtd);
+	cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
+}
+
+static void cafe_select_chip(struct mtd_info *mtd, int chipnr)
+{
+	//struct cafe_priv *cafe = mtd->priv;
+	//	cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr);
+}
+
+static int cafe_nand_interrupt(int irq, void *id)
+{
+	struct mtd_info *mtd = id;
+	struct cafe_priv *cafe = mtd->priv;
+	uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
+	cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ);
+	if (!irqs)
+		return IRQ_NONE;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ));
+	return IRQ_HANDLED;
+}
+
+static void cafe_nand_bug(struct mtd_info *mtd)
+{
+	BUG();
+}
+
+static int cafe_nand_write_oob(struct mtd_info *mtd,
+			       struct nand_chip *chip, int page)
+{
+	int status = 0;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+	status = chip->waitfunc(mtd, chip);
+
+	return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+/* Don't use -- use nand_read_oob_std for now */
+static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			      int page, int sndcmd)
+{
+	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	return 1;
+}
+/**
+ * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ *
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
+ */
+static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+			       uint8_t *buf)
+{
+	struct cafe_priv *cafe = mtd->priv;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n",
+		     cafe_readl(cafe, NAND_ECC_RESULT),
+		     cafe_readl(cafe, NAND_ECC_SYN01));
+
+	chip->read_buf(mtd, buf, mtd->writesize);
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
+		unsigned short syn[8];
+		int i;
+
+		for (i=0; i<8; i+=2) {
+			uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
+			syn[i] = tmp & 0xfff;
+			syn[i+1] = (tmp >> 16) & 0xfff;
+		}
+
+		if ((i = cafe_correct_ecc(buf, syn)) < 0) {
+			dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
+				cafe_readl(cafe, NAND_ADDR2) * 2048);
+			for (i=0; i< 0x5c; i+=4)
+				printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
+			mtd->ecc_stats.failed++;
+		} else {
+			dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", i);
+			mtd->ecc_stats.corrected += i;
+		}
+	}
+
+
+	return 0;
+}
+
+static struct nand_ecclayout cafe_oobinfo_2048 = {
+	.eccbytes = 14,
+	.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
+	.oobfree = {{14, 50}}
+};
+
+/* Ick. The BBT code really ought to be able to work this bit out
+   for itself from the above, at least for the 2KiB case */
+static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' };
+static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' };
+
+static uint8_t cafe_bbt_pattern_512[] = { 0xBB };
+static uint8_t cafe_mirror_pattern_512[] = { 0xBC };
+
+
+static struct nand_bbt_descr cafe_bbt_main_descr_2048 = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	14,
+	.len = 4,
+	.veroffs = 18,
+	.maxblocks = 4,
+	.pattern = cafe_bbt_pattern_2048
+};
+
+static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	14,
+	.len = 4,
+	.veroffs = 18,
+	.maxblocks = 4,
+	.pattern = cafe_mirror_pattern_2048
+};
+
+static struct nand_ecclayout cafe_oobinfo_512 = {
+	.eccbytes = 14,
+	.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
+	.oobfree = {{14, 2}}
+};
+
+static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	14,
+	.len = 1,
+	.veroffs = 15,
+	.maxblocks = 4,
+	.pattern = cafe_bbt_pattern_512
+};
+
+static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	14,
+	.len = 1,
+	.veroffs = 15,
+	.maxblocks = 4,
+	.pattern = cafe_mirror_pattern_512
+};
+
+
+static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
+					  struct nand_chip *chip, const uint8_t *buf)
+{
+	struct cafe_priv *cafe = mtd->priv;
+
+	chip->write_buf(mtd, buf, mtd->writesize);
+	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	/* Set up ECC autogeneration */
+	cafe->ctl2 |= (1<<30);
+}
+
+static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf, int page, int cached, int raw)
+{
+	int status;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+	if (unlikely(raw))
+		chip->ecc.write_page_raw(mtd, chip, buf);
+	else
+		chip->ecc.write_page(mtd, chip, buf);
+
+	/*
+	 * Cached progamming disabled for now, Not sure if its worth the
+	 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+	 */
+	cached = 0;
+
+	if (!cached || !(chip->options & NAND_CACHEPRG)) {
+
+		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+		status = chip->waitfunc(mtd, chip);
+		/*
+		 * See if operation failed and additional status checks are
+		 * available
+		 */
+		if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+			status = chip->errstat(mtd, chip, FL_WRITING, status,
+					       page);
+
+		if (status & NAND_STATUS_FAIL)
+			return -EIO;
+	} else {
+		chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+		status = chip->waitfunc(mtd, chip);
+	}
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+	/* Send command to read back the data */
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+	if (chip->verify_buf(mtd, buf, mtd->writesize))
+		return -EIO;
+#endif
+	return 0;
+}
+
+static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	return 0;
+}
+
+static int __devinit cafe_nand_probe(struct pci_dev *pdev,
+				     const struct pci_device_id *ent)
+{
+	struct mtd_info *mtd;
+	struct cafe_priv *cafe;
+	uint32_t ctrl;
+	int err = 0;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	pci_set_master(pdev);
+
+	mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL);
+	if (!mtd) {
+		dev_warn(&pdev->dev, "failed to alloc mtd_info\n");
+		return  -ENOMEM;
+	}
+	cafe = (void *)(&mtd[1]);
+
+	mtd->priv = cafe;
+	mtd->owner = THIS_MODULE;
+
+	cafe->pdev = pdev;
+	cafe->mmio = pci_iomap(pdev, 0, 0);
+	if (!cafe->mmio) {
+		dev_warn(&pdev->dev, "failed to iomap\n");
+		err = -ENOMEM;
+		goto out_free_mtd;
+	}
+	cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers),
+					  &cafe->dmaaddr, GFP_KERNEL);
+	if (!cafe->dmabuf) {
+		err = -ENOMEM;
+		goto out_ior;
+	}
+	cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
+
+	cafe->nand.cmdfunc = cafe_nand_cmdfunc;
+	cafe->nand.dev_ready = cafe_device_ready;
+	cafe->nand.read_byte = cafe_read_byte;
+	cafe->nand.read_buf = cafe_read_buf;
+	cafe->nand.write_buf = cafe_write_buf;
+	cafe->nand.select_chip = cafe_select_chip;
+
+	cafe->nand.chip_delay = 0;
+
+	/* Enable the following for a flash based bad block table */
+	cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS;
+
+	if (skipbbt) {
+		cafe->nand.options |= NAND_SKIP_BBTSCAN;
+		cafe->nand.block_bad = cafe_nand_block_bad;
+	}
+
+	/* 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);
+
+	/* Timings from Marvell's test code (not verified or calculated by us) */
+	if (!slowtiming) {
+		cafe_writel(cafe, 0x01010a0a, NAND_TIMING1);
+		cafe_writel(cafe, 0x24121212, NAND_TIMING2);
+		cafe_writel(cafe, 0x11000000, NAND_TIMING3);
+	} else {
+		cafe_writel(cafe, 0xffffffff, NAND_TIMING1);
+		cafe_writel(cafe, 0xffffffff, NAND_TIMING2);
+		cafe_writel(cafe, 0xffffffff, NAND_TIMING3);
+	}
+	cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
+	err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd);
+	if (err) {
+		dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
+
+		goto out_free_dma;
+	}
+#if 1
+	/* 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);
+
+	cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
+		cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
+
+	/* Enable NAND IRQ in global IRQ mask register */
+	cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+	cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
+		cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
+#endif
+#if 1
+	mtd->writesize=2048;
+	mtd->oobsize = 0x40;
+	memset(cafe->dmabuf, 0x5a, 2112);
+	cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1);
+	cafe->nand.read_byte(mtd);
+	cafe->nand.read_byte(mtd);
+	cafe->nand.read_byte(mtd);
+	cafe->nand.read_byte(mtd);
+	cafe->nand.read_byte(mtd);
+#endif
+#if 0
+	cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0);
+	//	nand_wait_ready(mtd);
+	cafe->nand.read_byte(mtd);
+	cafe->nand.read_byte(mtd);
+	cafe->nand.read_byte(mtd);
+	cafe->nand.read_byte(mtd);
+#endif
+#if 0
+	writel(0x84600070, cafe->mmio);
+	udelay(10);
+	cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM));
+#endif
+	/* Scan to find existance of the device */
+	if (nand_scan_ident(mtd, 1)) {
+		err = -ENXIO;
+		goto out_irq;
+	}
+
+	cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
+	if (mtd->writesize == 2048)
+		cafe->ctl2 |= 1<<29; /* 2KiB page size */
+
+	/* Set up ECC according to the type of chip we found */
+	if (mtd->writesize == 2048) {
+		cafe->nand.ecc.layout = &cafe_oobinfo_2048;
+		cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
+		cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
+	} else if (mtd->writesize == 512) {
+		cafe->nand.ecc.layout = &cafe_oobinfo_512;
+		cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
+		cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
+	} else {
+		printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
+		       mtd->writesize);
+		goto out_irq;
+	}
+	cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
+	cafe->nand.ecc.size = mtd->writesize;
+	cafe->nand.ecc.bytes = 14;
+	cafe->nand.ecc.hwctl  = (void *)cafe_nand_bug;
+	cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
+	cafe->nand.ecc.correct  = (void *)cafe_nand_bug;
+	cafe->nand.write_page = cafe_nand_write_page;
+	cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
+	cafe->nand.ecc.write_oob = cafe_nand_write_oob;
+	cafe->nand.ecc.read_page = cafe_nand_read_page;
+	cafe->nand.ecc.read_oob = cafe_nand_read_oob;
+
+	err = nand_scan_tail(mtd);
+	if (err)
+		goto out_irq;
+
+	pci_set_drvdata(pdev, mtd);
+	add_mtd_device(mtd);
+	goto out;
+
+ out_irq:
+	/* Disable NAND IRQ in global IRQ mask register */
+	cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
+	free_irq(pdev->irq, mtd);
+ out_free_dma:
+	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+ out_ior:
+	pci_iounmap(pdev, cafe->mmio);
+ out_free_mtd:
+	kfree(mtd);
+ out:
+	return err;
+}
+
+static void __devexit cafe_nand_remove(struct pci_dev *pdev)
+{
+	struct mtd_info *mtd = pci_get_drvdata(pdev);
+	struct cafe_priv *cafe = mtd->priv;
+
+	del_mtd_device(mtd);
+	/* Disable NAND IRQ in global IRQ mask register */
+	cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
+	free_irq(pdev->irq, mtd);
+	nand_release(mtd);
+	pci_iounmap(pdev, cafe->mmio);
+	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+	kfree(mtd);
+}
+
+static struct pci_device_id cafe_nand_tbl[] = {
+	{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 }
+};
+
+MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
+
+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)
+{
+	return pci_register_driver(&cafe_nand_pci_driver);
+}
+
+static void cafe_nand_exit(void)
+{
+	pci_unregister_driver(&cafe_nand_pci_driver);
+}
+module_init(cafe_nand_init);
+module_exit(cafe_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip");
+
+/* Correct ECC for 2048 bytes of 0xff:
+   41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */
+
+/* dwmw2's B-test board, in case of completely screwing it:
+Bad eraseblock 2394 at 0x12b40000
+Bad eraseblock 2627 at 0x14860000
+Bad eraseblock 3349 at 0x1a2a0000
+*/

+ 1381 - 0
drivers/mtd/nand/cafe_ecc.c

@@ -0,0 +1,1381 @@
+/* Error correction for CAFÉ NAND controller
+ *
+ * © 2006 Marvell, Inc.
+ * Author: Tom Chiou
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+static unsigned short gf4096_mul(unsigned short, unsigned short);
+static unsigned short gf64_mul(unsigned short, unsigned short);
+static unsigned short gf4096_inv(unsigned short);
+static unsigned short err_pos(unsigned short);
+static void find_4bit_err_coefs(unsigned short, unsigned short, unsigned short,
+				unsigned short, unsigned short, unsigned short,
+				unsigned short, unsigned short, unsigned short *);
+static void zero_4x5_col3(unsigned short[4][5]);
+static void zero_4x5_col2(unsigned short[4][5]);
+static void zero_4x5_col1(unsigned short[4][5]);
+static void swap_4x5_rows(unsigned short[4][5], int, int, int);
+static void swap_2x3_rows(unsigned short m[2][3]);
+static void solve_4x5(unsigned short m[4][5], unsigned short *, int *);
+static void sort_coefs(int *, unsigned short *, int);
+static void find_4bit_err_pats(unsigned short, unsigned short, unsigned short,
+			       unsigned short, unsigned short, unsigned short,
+			       unsigned short, unsigned short, unsigned short *);
+static void find_3bit_err_coefs(unsigned short, unsigned short, unsigned short,
+				unsigned short, unsigned short, unsigned short,
+				unsigned short *);
+static void zero_3x4_col2(unsigned short[3][4]);
+static void zero_3x4_col1(unsigned short[3][4]);
+static void swap_3x4_rows(unsigned short[3][4], int, int, int);
+static void solve_3x4(unsigned short[3][4], unsigned short *, int *);
+static void find_3bit_err_pats(unsigned short, unsigned short, unsigned short,
+			       unsigned short, unsigned short, unsigned short,
+			       unsigned short *);
+
+static void find_2bit_err_pats(unsigned short, unsigned short, unsigned short,
+			       unsigned short, unsigned short *);
+static void find_2x2_soln(unsigned short, unsigned short, unsigned short,
+			  unsigned short, unsigned short, unsigned short,
+			  unsigned short *);
+static void solve_2x3(unsigned short[2][3], unsigned short *);
+static int chk_no_err_only(unsigned short *, unsigned short *);
+static int chk_1_err_only(unsigned short *, unsigned short *);
+static int chk_2_err_only(unsigned short *, unsigned short *);
+static int chk_3_err_only(unsigned short *, unsigned short *);
+static int chk_4_err_only(unsigned short *, unsigned short *);
+
+static unsigned short gf64_mul(unsigned short a, unsigned short b)
+{
+	unsigned short tmp1, tmp2, tmp3, tmp4, tmp5;
+	unsigned short c_bit0, c_bit1, c_bit2, c_bit3, c_bit4, c_bit5, c;
+
+	tmp1 = ((a) ^ (a >> 5));
+	tmp2 = ((a >> 4) ^ (a >> 5));
+	tmp3 = ((a >> 3) ^ (a >> 4));
+	tmp4 = ((a >> 2) ^ (a >> 3));
+	tmp5 = ((a >> 1) ^ (a >> 2));
+
+	c_bit0 = ((a & b) ^ ((a >> 5) & (b >> 1)) ^ ((a >> 4) & (b >> 2)) ^
+		  ((a >> 3) & (b >> 3)) ^ ((a >> 2) & (b >> 4)) ^ ((a >> 1) & (b >> 5))) & 0x1;
+
+	c_bit1 = (((a >> 1) & b) ^ (tmp1 & (b >> 1)) ^ (tmp2 & (b >> 2)) ^
+		  (tmp3 & (b >> 3)) ^ (tmp4 & (b >> 4)) ^ (tmp5 & (b >> 5))) & 0x1;
+
+	c_bit2 = (((a >> 2) & b) ^ ((a >> 1) & (b >> 1)) ^ (tmp1 & (b >> 2)) ^
+		  (tmp2 & (b >> 3)) ^ (tmp3 & (b >> 4)) ^ (tmp4 & (b >> 5))) & 0x1;
+
+	c_bit3 = (((a >> 3) & b) ^ ((a >> 2) & (b >> 1)) ^ ((a >> 1) & (b >> 2)) ^
+		  (tmp1 & (b >> 3)) ^ (tmp2 & (b >> 4)) ^ (tmp3 & (b >> 5))) & 0x1;
+
+	c_bit4 = (((a >> 4) & b) ^ ((a >> 3) & (b >> 1)) ^ ((a >> 2) & (b >> 2)) ^
+		  ((a >> 1) & (b >> 3)) ^ (tmp1 & (b >> 4)) ^ (tmp2 & (b >> 5))) & 0x1;
+
+	c_bit5 = (((a >> 5) & b) ^ ((a >> 4) & (b >> 1)) ^ ((a >> 3) & (b >> 2)) ^
+		  ((a >> 2) & (b >> 3)) ^ ((a >> 1) & (b >> 4)) ^ (tmp1 & (b >> 5))) & 0x1;
+
+	c = c_bit0 | (c_bit1 << 1) | (c_bit2 << 2) | (c_bit3 << 3) | (c_bit4 << 4) | (c_bit5 << 5);
+
+	return c;
+}
+
+static unsigned short gf4096_mul(unsigned short a, unsigned short b)
+{
+	unsigned short ah, al, bh, bl, alxah, blxbh, ablh, albl, ahbh, ahbhB, c;
+
+	ah = (a >> 6) & 0x3f;
+	al = a & 0x3f;
+	bh = (b >> 6) & 0x3f;
+	bl = b & 0x3f;
+	alxah = al ^ ah;
+	blxbh = bl ^ bh;
+
+	ablh = gf64_mul(alxah, blxbh);
+	albl = gf64_mul(al, bl);
+	ahbh = gf64_mul(ah, bh);
+
+	ahbhB = ((ahbh & 0x1) << 5) |
+	    ((ahbh & 0x20) >> 1) |
+	    ((ahbh & 0x10) >> 1) | ((ahbh & 0x8) >> 1) | ((ahbh & 0x4) >> 1) | (((ahbh >> 1) ^ ahbh) & 0x1);
+
+	c = ((ablh ^ albl) << 6) | (ahbhB ^ albl);
+	return c;
+}
+
+static void find_2bit_err_pats(unsigned short s0, unsigned short s1, unsigned short r0, unsigned short r1, unsigned short *pats)
+{
+	find_2x2_soln(0x1, 0x1, r0, r1, s0, s1, pats);
+}
+
+static void find_3bit_err_coefs(unsigned short s0, unsigned short s1,
+				unsigned short s2, unsigned short s3, unsigned short s4, unsigned short s5, unsigned short *coefs)
+{
+	unsigned short m[3][4];
+	int row_order[3];
+
+	row_order[0] = 0;
+	row_order[1] = 1;
+	row_order[2] = 2;
+	m[0][0] = s2;
+	m[0][1] = s1;
+	m[0][2] = s0;
+	m[0][3] = s3;
+	m[1][0] = s3;
+	m[1][1] = s2;
+	m[1][2] = s1;
+	m[1][3] = s4;
+	m[2][0] = s4;
+	m[2][1] = s3;
+	m[2][2] = s2;
+	m[2][3] = s5;
+
+	if (m[0][2] != 0x0) {
+		zero_3x4_col2(m);
+	} else if (m[1][2] != 0x0) {
+		swap_3x4_rows(m, 0, 1, 4);
+		zero_3x4_col2(m);
+	} else if (m[2][2] != 0x0) {
+		swap_3x4_rows(m, 0, 2, 4);
+		zero_3x4_col2(m);
+	} else {
+		printk(KERN_ERR "Error: find_3bit_err_coefs, s0,s1,s2 all zeros!\n");
+	}
+
+	if (m[1][1] != 0x0) {
+		zero_3x4_col1(m);
+	} else if (m[2][1] != 0x0) {
+		swap_3x4_rows(m, 1, 2, 4);
+		zero_3x4_col1(m);
+	} else {
+		printk(KERN_ERR "Error: find_3bit_err_coefs, cannot resolve col 1!\n");
+	}
+
+	/* solve coefs */
+	solve_3x4(m, coefs, row_order);
+}
+
+static void zero_3x4_col2(unsigned short m[3][4])
+{
+	unsigned short minv1, minv2;
+
+	minv1 = gf4096_mul(m[1][2], gf4096_inv(m[0][2]));
+	minv2 = gf4096_mul(m[2][2], gf4096_inv(m[0][2]));
+	m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv1);
+	m[1][1] = m[1][1] ^ gf4096_mul(m[0][1], minv1);
+	m[1][3] = m[1][3] ^ gf4096_mul(m[0][3], minv1);
+	m[2][0] = m[2][0] ^ gf4096_mul(m[0][0], minv2);
+	m[2][1] = m[2][1] ^ gf4096_mul(m[0][1], minv2);
+	m[2][3] = m[2][3] ^ gf4096_mul(m[0][3], minv2);
+}
+
+static void zero_3x4_col1(unsigned short m[3][4])
+{
+	unsigned short minv;
+	minv = gf4096_mul(m[2][1], gf4096_inv(m[1][1]));
+	m[2][0] = m[2][0] ^ gf4096_mul(m[1][0], minv);
+	m[2][3] = m[2][3] ^ gf4096_mul(m[1][3], minv);
+}
+
+static void swap_3x4_rows(unsigned short m[3][4], int i, int j, int col_width)
+{
+	unsigned short tmp0;
+	int cnt;
+	for (cnt = 0; cnt < col_width; cnt++) {
+		tmp0 = m[i][cnt];
+		m[i][cnt] = m[j][cnt];
+		m[j][cnt] = tmp0;
+	}
+}
+
+static void solve_3x4(unsigned short m[3][4], unsigned short *coefs, int *row_order)
+{
+	unsigned short tmp[3];
+	tmp[0] = gf4096_mul(m[2][3], gf4096_inv(m[2][0]));
+	tmp[1] = gf4096_mul((gf4096_mul(tmp[0], m[1][0]) ^ m[1][3]), gf4096_inv(m[1][1]));
+	tmp[2] = gf4096_mul((gf4096_mul(tmp[0], m[0][0]) ^ gf4096_mul(tmp[1], m[0][1]) ^ m[0][3]), gf4096_inv(m[0][2]));
+	sort_coefs(row_order, tmp, 3);
+	coefs[0] = tmp[0];
+	coefs[1] = tmp[1];
+	coefs[2] = tmp[2];
+}
+
+static void find_3bit_err_pats(unsigned short s0, unsigned short s1,
+			       unsigned short s2, unsigned short r0,
+			       unsigned short r1, unsigned short r2,
+			       unsigned short *pats)
+{
+	find_2x2_soln(r0 ^ r2, r1 ^ r2,
+		      gf4096_mul(r0, r0 ^ r2), gf4096_mul(r1, r1 ^ r2),
+		      gf4096_mul(s0, r2) ^ s1, gf4096_mul(s1, r2) ^ s2, pats);
+	pats[2] = s0 ^ pats[0] ^ pats[1];
+}
+
+static void find_4bit_err_coefs(unsigned short s0, unsigned short s1,
+				unsigned short s2, unsigned short s3,
+				unsigned short s4, unsigned short s5,
+				unsigned short s6, unsigned short s7,
+				unsigned short *coefs)
+{
+	unsigned short m[4][5];
+	int row_order[4];
+
+	row_order[0] = 0;
+	row_order[1] = 1;
+	row_order[2] = 2;
+	row_order[3] = 3;
+
+	m[0][0] = s3;
+	m[0][1] = s2;
+	m[0][2] = s1;
+	m[0][3] = s0;
+	m[0][4] = s4;
+	m[1][0] = s4;
+	m[1][1] = s3;
+	m[1][2] = s2;
+	m[1][3] = s1;
+	m[1][4] = s5;
+	m[2][0] = s5;
+	m[2][1] = s4;
+	m[2][2] = s3;
+	m[2][3] = s2;
+	m[2][4] = s6;
+	m[3][0] = s6;
+	m[3][1] = s5;
+	m[3][2] = s4;
+	m[3][3] = s3;
+	m[3][4] = s7;
+
+	if (m[0][3] != 0x0) {
+		zero_4x5_col3(m);
+	} else if (m[1][3] != 0x0) {
+		swap_4x5_rows(m, 0, 1, 5);
+		zero_4x5_col3(m);
+	} else if (m[2][3] != 0x0) {
+		swap_4x5_rows(m, 0, 2, 5);
+		zero_4x5_col3(m);
+	} else if (m[3][3] != 0x0) {
+		swap_4x5_rows(m, 0, 3, 5);
+		zero_4x5_col3(m);
+	} else {
+		printk(KERN_ERR "Error: find_4bit_err_coefs, s0,s1,s2,s3 all zeros!\n");
+	}
+
+	if (m[1][2] != 0x0) {
+		zero_4x5_col2(m);
+	} else if (m[2][2] != 0x0) {
+		swap_4x5_rows(m, 1, 2, 5);
+		zero_4x5_col2(m);
+	} else if (m[3][2] != 0x0) {
+		swap_4x5_rows(m, 1, 3, 5);
+		zero_4x5_col2(m);
+	} else {
+		printk(KERN_ERR "Error: find_4bit_err_coefs, cannot resolve col 2!\n");
+	}
+
+	if (m[2][1] != 0x0) {
+		zero_4x5_col1(m);
+	} else if (m[3][1] != 0x0) {
+		swap_4x5_rows(m, 2, 3, 5);
+		zero_4x5_col1(m);
+	} else {
+		printk(KERN_ERR "Error: find_4bit_err_coefs, cannot resolve col 1!\n");
+	}
+
+	solve_4x5(m, coefs, row_order);
+}
+
+static void zero_4x5_col3(unsigned short m[4][5])
+{
+	unsigned short minv1, minv2, minv3;
+
+	minv1 = gf4096_mul(m[1][3], gf4096_inv(m[0][3]));
+	minv2 = gf4096_mul(m[2][3], gf4096_inv(m[0][3]));
+	minv3 = gf4096_mul(m[3][3], gf4096_inv(m[0][3]));
+
+	m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv1);
+	m[1][1] = m[1][1] ^ gf4096_mul(m[0][1], minv1);
+	m[1][2] = m[1][2] ^ gf4096_mul(m[0][2], minv1);
+	m[1][4] = m[1][4] ^ gf4096_mul(m[0][4], minv1);
+	m[2][0] = m[2][0] ^ gf4096_mul(m[0][0], minv2);
+	m[2][1] = m[2][1] ^ gf4096_mul(m[0][1], minv2);
+	m[2][2] = m[2][2] ^ gf4096_mul(m[0][2], minv2);
+	m[2][4] = m[2][4] ^ gf4096_mul(m[0][4], minv2);
+	m[3][0] = m[3][0] ^ gf4096_mul(m[0][0], minv3);
+	m[3][1] = m[3][1] ^ gf4096_mul(m[0][1], minv3);
+	m[3][2] = m[3][2] ^ gf4096_mul(m[0][2], minv3);
+	m[3][4] = m[3][4] ^ gf4096_mul(m[0][4], minv3);
+}
+
+static void zero_4x5_col2(unsigned short m[4][5])
+{
+	unsigned short minv2, minv3;
+
+	minv2 = gf4096_mul(m[2][2], gf4096_inv(m[1][2]));
+	minv3 = gf4096_mul(m[3][2], gf4096_inv(m[1][2]));
+
+	m[2][0] = m[2][0] ^ gf4096_mul(m[1][0], minv2);
+	m[2][1] = m[2][1] ^ gf4096_mul(m[1][1], minv2);
+	m[2][4] = m[2][4] ^ gf4096_mul(m[1][4], minv2);
+	m[3][0] = m[3][0] ^ gf4096_mul(m[1][0], minv3);
+	m[3][1] = m[3][1] ^ gf4096_mul(m[1][1], minv3);
+	m[3][4] = m[3][4] ^ gf4096_mul(m[1][4], minv3);
+}
+
+static void zero_4x5_col1(unsigned short m[4][5])
+{
+	unsigned short minv;
+
+	minv = gf4096_mul(m[3][1], gf4096_inv(m[2][1]));
+
+	m[3][0] = m[3][0] ^ gf4096_mul(m[2][0], minv);
+	m[3][4] = m[3][4] ^ gf4096_mul(m[2][4], minv);
+}
+
+static void swap_4x5_rows(unsigned short m[4][5], int i, int j, int col_width)
+{
+	unsigned short tmp0;
+	int cnt;
+
+	for (cnt = 0; cnt < col_width; cnt++) {
+		tmp0 = m[i][cnt];
+		m[i][cnt] = m[j][cnt];
+		m[j][cnt] = tmp0;
+	}
+}
+
+static void solve_4x5(unsigned short m[4][5], unsigned short *coefs, int *row_order)
+{
+	unsigned short tmp[4];
+
+	tmp[0] = gf4096_mul(m[3][4], gf4096_inv(m[3][0]));
+	tmp[1] = gf4096_mul((gf4096_mul(tmp[0], m[2][0]) ^ m[2][4]), gf4096_inv(m[2][1]));
+	tmp[2] = gf4096_mul((gf4096_mul(tmp[0], m[1][0]) ^ gf4096_mul(tmp[1], m[1][1]) ^ m[1][4]), gf4096_inv(m[1][2]));
+	tmp[3] = gf4096_mul((gf4096_mul(tmp[0], m[0][0]) ^
+			gf4096_mul(tmp[1], m[0][1]) ^ gf4096_mul(tmp[2], m[0][2]) ^ m[0][4]), gf4096_inv(m[0][3]));
+	sort_coefs(row_order, tmp, 4);
+	coefs[0] = tmp[0];
+	coefs[1] = tmp[1];
+	coefs[2] = tmp[2];
+	coefs[3] = tmp[3];
+}
+
+static void sort_coefs(int *order, unsigned short *soln, int len)
+{
+	int cnt, start_cnt, least_ord, least_cnt;
+	unsigned short tmp0;
+	for (start_cnt = 0; start_cnt < len; start_cnt++) {
+		for (cnt = start_cnt; cnt < len; cnt++) {
+			if (cnt == start_cnt) {
+				least_ord = order[cnt];
+				least_cnt = start_cnt;
+			} else {
+				if (least_ord > order[cnt]) {
+					least_ord = order[cnt];
+					least_cnt = cnt;
+				}
+			}
+		}
+		if (least_cnt != start_cnt) {
+			tmp0 = order[least_cnt];
+			order[least_cnt] = order[start_cnt];
+			order[start_cnt] = tmp0;
+			tmp0 = soln[least_cnt];
+			soln[least_cnt] = soln[start_cnt];
+			soln[start_cnt] = tmp0;
+		}
+	}
+}
+
+static void find_4bit_err_pats(unsigned short s0, unsigned short s1,
+			       unsigned short s2, unsigned short s3,
+			       unsigned short z1, unsigned short z2,
+			       unsigned short z3, unsigned short z4,
+			       unsigned short *pats)
+{
+	unsigned short z4_z1, z3z4_z3z3, z4_z2, s0z4_s1, z1z4_z1z1,
+		z4_z3, z2z4_z2z2, s1z4_s2, z3z3z4_z3z3z3, z1z1z4_z1z1z1, z2z2z4_z2z2z2, s2z4_s3;
+	unsigned short tmp0, tmp1, tmp2, tmp3;
+
+	z4_z1 = z4 ^ z1;
+	z3z4_z3z3 = gf4096_mul(z3, z4) ^ gf4096_mul(z3, z3);
+	z4_z2 = z4 ^ z2;
+	s0z4_s1 = gf4096_mul(s0, z4) ^ s1;
+	z1z4_z1z1 = gf4096_mul(z1, z4) ^ gf4096_mul(z1, z1);
+	z4_z3 = z4 ^ z3;
+	z2z4_z2z2 = gf4096_mul(z2, z4) ^ gf4096_mul(z2, z2);
+	s1z4_s2 = gf4096_mul(s1, z4) ^ s2;
+	z3z3z4_z3z3z3 = gf4096_mul(gf4096_mul(z3, z3), z4) ^ gf4096_mul(gf4096_mul(z3, z3), z3);
+	z1z1z4_z1z1z1 = gf4096_mul(gf4096_mul(z1, z1), z4) ^ gf4096_mul(gf4096_mul(z1, z1), z1);
+	z2z2z4_z2z2z2 = gf4096_mul(gf4096_mul(z2, z2), z4) ^ gf4096_mul(gf4096_mul(z2, z2), z2);
+	s2z4_s3 = gf4096_mul(s2, z4) ^ s3;
+
+	//find err pat 0,1
+	find_2x2_soln(gf4096_mul(z4_z1, z3z4_z3z3) ^
+		      gf4096_mul(z1z4_z1z1, z4_z3), gf4096_mul(z4_z2,
+							       z3z4_z3z3) ^
+		      gf4096_mul(z2z4_z2z2, z4_z3), gf4096_mul(z1z4_z1z1,
+							       z3z3z4_z3z3z3) ^
+		      gf4096_mul(z1z1z4_z1z1z1, z3z4_z3z3),
+		      gf4096_mul(z2z4_z2z2,
+				 z3z3z4_z3z3z3) ^ gf4096_mul(z2z2z4_z2z2z2,
+							     z3z4_z3z3),
+		      gf4096_mul(s0z4_s1, z3z4_z3z3) ^ gf4096_mul(s1z4_s2,
+								  z4_z3),
+		      gf4096_mul(s1z4_s2, z3z3z4_z3z3z3) ^ gf4096_mul(s2z4_s3, z3z4_z3z3), pats);
+	tmp0 = pats[0];
+	tmp1 = pats[1];
+	tmp2 = pats[0] ^ pats[1] ^ s0;
+	tmp3 = gf4096_mul(pats[0], z1) ^ gf4096_mul(pats[1], z2) ^ s1;
+
+	//find err pat 2,3
+	find_2x2_soln(0x1, 0x1, z3, z4, tmp2, tmp3, pats);
+	pats[2] = pats[0];
+	pats[3] = pats[1];
+	pats[0] = tmp0;
+	pats[1] = tmp1;
+}
+
+static void find_2x2_soln(unsigned short c00, unsigned short c01,
+			  unsigned short c10, unsigned short c11,
+			  unsigned short lval0, unsigned short lval1,
+			  unsigned short *soln)
+{
+	unsigned short m[2][3];
+	m[0][0] = c00;
+	m[0][1] = c01;
+	m[0][2] = lval0;
+	m[1][0] = c10;
+	m[1][1] = c11;
+	m[1][2] = lval1;
+
+	if (m[0][1] != 0x0) {
+		/* */
+	} else if (m[1][1] != 0x0) {
+		swap_2x3_rows(m);
+	} else {
+		printk(KERN_ERR "Warning: find_2bit_err_coefs, s0,s1 all zeros!\n");
+	}
+
+	solve_2x3(m, soln);
+}
+
+static void swap_2x3_rows(unsigned short m[2][3])
+{
+	unsigned short tmp0;
+	int cnt;
+
+	for (cnt = 0; cnt < 3; cnt++) {
+		tmp0 = m[0][cnt];
+		m[0][cnt] = m[1][cnt];
+		m[1][cnt] = tmp0;
+	}
+}
+
+static void solve_2x3(unsigned short m[2][3], unsigned short *coefs)
+{
+	unsigned short minv;
+
+	minv = gf4096_mul(m[1][1], gf4096_inv(m[0][1]));
+	m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv);
+	m[1][2] = m[1][2] ^ gf4096_mul(m[0][2], minv);
+	coefs[0] = gf4096_mul(m[1][2], gf4096_inv(m[1][0]));
+	coefs[1] = gf4096_mul((gf4096_mul(coefs[0], m[0][0]) ^ m[0][2]), gf4096_inv(m[0][1]));
+}
+
+static unsigned char gf64_inv[64] = {
+	 0,  1, 33, 62, 49, 43, 31, 44, 57, 37, 52, 28, 46, 40, 22, 25,
+	61, 54, 51, 39, 26, 35, 14, 24, 23, 15, 20, 34, 11, 53, 45,  6,
+	63,  2, 27, 21, 56,  9, 50, 19, 13, 47, 48,  5,  7, 30, 12, 41,
+	42,  4, 38, 18, 10, 29, 17, 60, 36,  8, 59, 58, 55, 16,  3, 32
+};
+
+static unsigned short gf4096_inv(unsigned short din)
+{
+	unsigned short alahxal, ah2B, deno, inv, bl, bh;
+	unsigned short ah, al, ahxal;
+	unsigned short dout;
+
+	ah = (din >> 6) & 0x3f;
+	al = din & 0x3f;
+	ahxal = ah ^ al;
+	ah2B = (((ah ^ (ah >> 3)) & 0x1) << 5) |
+		((ah >> 1) & 0x10) |
+		((((ah >> 5) ^ (ah >> 2)) & 0x1) << 3) |
+		((ah >> 2) & 0x4) | ((((ah >> 4) ^ (ah >> 1)) & 0x1) << 1) | (ah & 0x1);
+	alahxal = gf64_mul(ahxal, al);
+	deno = alahxal ^ ah2B;
+	inv = gf64_inv[deno];
+	bl = gf64_mul(inv, ahxal);
+	bh = gf64_mul(inv, ah);
+	dout = ((bh & 0x3f) << 6) | (bl & 0x3f);
+	return (((bh & 0x3f) << 6) | (bl & 0x3f));
+}
+
+static unsigned short err_pos_lut[4096] = {
+	0xfff, 0x000, 0x451, 0xfff, 0xfff, 0x3cf, 0xfff, 0x041,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x28a, 0xfff, 0x492, 0xfff,
+	0x145, 0xfff, 0xfff, 0x514, 0xfff, 0x082, 0xfff, 0xfff,
+	0xfff, 0x249, 0x38e, 0x410, 0xfff, 0x104, 0x208, 0x1c7,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x2cb, 0xfff, 0xfff, 0xfff,
+	0x0c3, 0x34d, 0x4d3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x186, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x30c, 0x555, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x166, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x385, 0x14e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e1,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x538, 0xfff, 0x16d, 0xfff,
+	0xfff, 0xfff, 0x45b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x29c, 0x2cc, 0x30b, 0x2b3, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0b3, 0xfff, 0x2f7,
+	0xfff, 0x32b, 0xfff, 0xfff, 0xfff, 0xfff, 0x0a7, 0xfff,
+	0xfff, 0x2da, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x07e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x11c, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x22f, 0xfff, 0x1f4, 0xfff, 0xfff,
+	0x2b0, 0x504, 0xfff, 0x114, 0xfff, 0xfff, 0xfff, 0x21d,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x00d, 0x3c4, 0x340, 0x10f,
+	0xfff, 0xfff, 0x266, 0x02e, 0xfff, 0xfff, 0xfff, 0x4f8,
+	0x337, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x07b, 0x168, 0xfff, 0xfff, 0x0fe,
+	0xfff, 0xfff, 0x51a, 0xfff, 0x458, 0xfff, 0x36d, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x073, 0x37d, 0x415, 0x550, 0xfff,
+	0xfff, 0xfff, 0x23b, 0x4b4, 0xfff, 0xfff, 0xfff, 0x1a1,
+	0xfff, 0xfff, 0x3aa, 0xfff, 0x117, 0x04d, 0x341, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x518, 0x03e, 0x0f2, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x363, 0xfff, 0x0b9, 0xfff, 0xfff,
+	0x241, 0xfff, 0xfff, 0x049, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x15f, 0x52d, 0xfff, 0xfff, 0xfff, 0x29e, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x4cf, 0x0fc, 0xfff, 0x36f, 0x3d3, 0xfff,
+	0x228, 0xfff, 0xfff, 0x45e, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x238, 0xfff, 0xfff, 0xfff, 0xfff, 0x47f, 0xfff, 0xfff,
+	0x43a, 0x265, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e8,
+	0xfff, 0xfff, 0x01a, 0xfff, 0xfff, 0xfff, 0xfff, 0x21e,
+	0x1fc, 0x40b, 0xfff, 0xfff, 0xfff, 0x2d0, 0x159, 0xfff,
+	0xfff, 0x313, 0xfff, 0xfff, 0x05c, 0x4cc, 0xfff, 0xfff,
+	0x0f6, 0x3d5, 0xfff, 0xfff, 0xfff, 0x54f, 0xfff, 0xfff,
+	0xfff, 0x172, 0x1e4, 0x07c, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x53c, 0x1ad, 0x535,
+	0x19b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x092, 0xfff, 0x2be, 0xfff, 0xfff, 0x482,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0e6, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x476, 0xfff, 0x51d, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x342, 0x2b5, 0x22e, 0x09a, 0xfff, 0x08d,
+	0x44f, 0x3ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d1, 0xfff,
+	0xfff, 0x543, 0xfff, 0x48f, 0xfff, 0x3d2, 0xfff, 0x0d5,
+	0x113, 0x0ec, 0x427, 0xfff, 0xfff, 0xfff, 0x4c4, 0xfff,
+	0xfff, 0x50a, 0xfff, 0x144, 0xfff, 0x105, 0x39f, 0x294,
+	0x164, 0xfff, 0x31a, 0xfff, 0xfff, 0x49a, 0xfff, 0x130,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x1be, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x49e, 0x371, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x0e8, 0x49c, 0x0f4, 0xfff,
+	0x338, 0x1a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x36c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x1ae, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x31b, 0xfff, 0xfff, 0x2dd, 0x522, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f4,
+	0x3c6, 0x30d, 0xfff, 0xfff, 0xfff, 0xfff, 0x34c, 0x18f,
+	0x30a, 0xfff, 0x01f, 0x079, 0xfff, 0xfff, 0x54d, 0x46b,
+	0x28c, 0x37f, 0xfff, 0xfff, 0xfff, 0xfff, 0x355, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x14f, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x359, 0x3fe, 0x3c5, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x423, 0xfff, 0xfff, 0x34a, 0x22c, 0xfff,
+	0x25a, 0xfff, 0xfff, 0x4ad, 0xfff, 0x28d, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x547, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x2e2, 0xfff, 0xfff, 0x1d5, 0xfff, 0x2a8, 0xfff, 0xfff,
+	0x03f, 0xfff, 0xfff, 0xfff, 0xfff, 0x3eb, 0x0fa, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x55b, 0xfff,
+	0x08e, 0xfff, 0x3ae, 0xfff, 0x3a4, 0xfff, 0x282, 0x158,
+	0xfff, 0x382, 0xfff, 0xfff, 0x499, 0xfff, 0xfff, 0x08a,
+	0xfff, 0xfff, 0xfff, 0x456, 0x3be, 0xfff, 0x1e2, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x559, 0xfff, 0x1a0, 0xfff,
+	0xfff, 0x0b4, 0xfff, 0xfff, 0xfff, 0x2df, 0xfff, 0xfff,
+	0xfff, 0x07f, 0x4f5, 0xfff, 0xfff, 0x27c, 0x133, 0x017,
+	0xfff, 0x3fd, 0xfff, 0xfff, 0xfff, 0x44d, 0x4cd, 0x17a,
+	0x0d7, 0x537, 0xfff, 0xfff, 0x353, 0xfff, 0xfff, 0x351,
+	0x366, 0xfff, 0x44a, 0xfff, 0x1a6, 0xfff, 0xfff, 0xfff,
+	0x291, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1e3,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x389, 0xfff, 0x07a, 0xfff,
+	0x1b6, 0x2ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x24e, 0x074,
+	0xfff, 0xfff, 0x3dc, 0xfff, 0x4e3, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x4eb, 0xfff, 0xfff, 0x3b8, 0x4de, 0xfff, 0x19c,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x262,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x076, 0x4e8, 0x3da,
+	0xfff, 0x531, 0xfff, 0xfff, 0x14a, 0xfff, 0x0a2, 0x433,
+	0x3df, 0x1e9, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e7, 0x285,
+	0x2d8, 0xfff, 0xfff, 0xfff, 0x349, 0x18d, 0x098, 0xfff,
+	0x0df, 0x4bf, 0xfff, 0xfff, 0x0b2, 0xfff, 0x346, 0x24d,
+	0xfff, 0xfff, 0xfff, 0x24f, 0x4fa, 0x2f9, 0xfff, 0xfff,
+	0x3c9, 0xfff, 0x2b4, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x056, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x179, 0xfff, 0x0e9, 0x3f0, 0x33d, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x1fd, 0xfff, 0xfff, 0x526, 0xfff,
+	0xfff, 0xfff, 0x53d, 0xfff, 0xfff, 0xfff, 0x170, 0x331,
+	0xfff, 0x068, 0xfff, 0xfff, 0xfff, 0x3f7, 0xfff, 0x3d8,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x09f, 0x556, 0xfff, 0xfff, 0x02d, 0xfff, 0xfff,
+	0x553, 0xfff, 0xfff, 0xfff, 0x1f0, 0xfff, 0xfff, 0x4d6,
+	0x41e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d5, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x248, 0xfff, 0xfff, 0xfff, 0x0a3,
+	0xfff, 0x217, 0xfff, 0xfff, 0xfff, 0x4f1, 0x209, 0xfff,
+	0xfff, 0x475, 0x234, 0x52b, 0x398, 0xfff, 0x08b, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x2c2, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x268, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x4a3, 0xfff, 0x0aa, 0xfff, 0x1d9, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x155, 0xfff, 0xfff, 0xfff, 0xfff, 0x0bf,
+	0x539, 0xfff, 0xfff, 0x2f1, 0x545, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x2a7, 0x06f, 0xfff, 0x378, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x25e, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x15d, 0x02a, 0xfff, 0xfff, 0x0bc,
+	0x235, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x150, 0xfff, 0x1a9, 0xfff, 0xfff, 0xfff, 0xfff, 0x381,
+	0xfff, 0x04e, 0x270, 0x13f, 0xfff, 0xfff, 0x405, 0xfff,
+	0x3cd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x2ef, 0xfff, 0x06a, 0xfff, 0xfff, 0xfff, 0x34f,
+	0x212, 0xfff, 0xfff, 0x0e2, 0xfff, 0x083, 0x298, 0xfff,
+	0xfff, 0xfff, 0x0c2, 0xfff, 0xfff, 0x52e, 0xfff, 0x488,
+	0xfff, 0xfff, 0xfff, 0x36b, 0xfff, 0xfff, 0xfff, 0x442,
+	0x091, 0xfff, 0x41c, 0xfff, 0xfff, 0x3a5, 0xfff, 0x4e6,
+	0xfff, 0xfff, 0x40d, 0x31d, 0xfff, 0xfff, 0xfff, 0x4c1,
+	0x053, 0xfff, 0x418, 0x13c, 0xfff, 0x350, 0xfff, 0x0ae,
+	0xfff, 0xfff, 0x41f, 0xfff, 0x470, 0xfff, 0x4ca, 0xfff,
+	0xfff, 0xfff, 0x02b, 0x450, 0xfff, 0x1f8, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x293, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x411, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x0b8, 0xfff, 0xfff, 0xfff,
+	0x3e1, 0xfff, 0xfff, 0xfff, 0xfff, 0x43c, 0xfff, 0x2b2,
+	0x2ab, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ec,
+	0xfff, 0xfff, 0xfff, 0x3f8, 0x034, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x11a, 0xfff, 0x541, 0x45c, 0x134,
+	0x1cc, 0xfff, 0xfff, 0xfff, 0x469, 0xfff, 0xfff, 0x44b,
+	0x161, 0xfff, 0xfff, 0xfff, 0x055, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x307, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d1, 0xfff,
+	0xfff, 0xfff, 0x124, 0x37b, 0x26b, 0x336, 0xfff, 0xfff,
+	0x2e4, 0x3cb, 0xfff, 0xfff, 0x0f8, 0x3c8, 0xfff, 0xfff,
+	0xfff, 0x461, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4b5,
+	0x2cf, 0xfff, 0xfff, 0xfff, 0x20f, 0xfff, 0x35a, 0xfff,
+	0x490, 0xfff, 0x185, 0xfff, 0xfff, 0xfff, 0xfff, 0x42e,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x54b, 0xfff, 0xfff, 0xfff,
+	0x146, 0xfff, 0x412, 0xfff, 0xfff, 0xfff, 0x1ff, 0xfff,
+	0xfff, 0x3e0, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d5, 0xfff,
+	0x4df, 0x505, 0xfff, 0x413, 0xfff, 0x1a5, 0xfff, 0x3b2,
+	0xfff, 0xfff, 0xfff, 0x35b, 0xfff, 0x116, 0xfff, 0xfff,
+	0x171, 0x4d0, 0xfff, 0x154, 0x12d, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x468, 0x4db, 0xfff,
+	0xfff, 0x1df, 0xfff, 0xfff, 0xfff, 0xfff, 0x05a, 0xfff,
+	0x0f1, 0x403, 0xfff, 0x22b, 0x2e0, 0xfff, 0xfff, 0xfff,
+	0x2b7, 0x373, 0xfff, 0xfff, 0xfff, 0xfff, 0x13e, 0xfff,
+	0xfff, 0xfff, 0x0d0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x329, 0x1d2, 0x3fa, 0x047, 0xfff, 0x2f2, 0xfff, 0xfff,
+	0x141, 0x0ac, 0x1d7, 0xfff, 0x07d, 0xfff, 0xfff, 0xfff,
+	0x1c1, 0xfff, 0x487, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x045, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x288, 0x0cd, 0xfff, 0xfff, 0xfff, 0xfff, 0x226, 0x1d8,
+	0xfff, 0x153, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4cb,
+	0x528, 0xfff, 0xfff, 0xfff, 0x20a, 0x343, 0x3a1, 0xfff,
+	0xfff, 0xfff, 0x2d7, 0x2d3, 0x1aa, 0x4c5, 0xfff, 0xfff,
+	0xfff, 0x42b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x3e9, 0xfff, 0x20b, 0x260,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x37c, 0x2fd,
+	0xfff, 0xfff, 0x2c8, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x31e, 0xfff, 0x335, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x135, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x35c, 0x4dd, 0x129, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x1ef, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x34e, 0xfff, 0xfff, 0xfff, 0xfff, 0x407, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x3ad, 0xfff, 0xfff, 0xfff,
+	0x379, 0xfff, 0xfff, 0x1d0, 0x38d, 0xfff, 0xfff, 0x1e8,
+	0x184, 0x3c1, 0x1c4, 0xfff, 0x1f9, 0xfff, 0xfff, 0x424,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x1d3, 0x0d4, 0xfff, 0x4e9,
+	0xfff, 0xfff, 0xfff, 0x530, 0x107, 0xfff, 0x106, 0x04f,
+	0xfff, 0xfff, 0x4c7, 0x503, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x15c, 0xfff, 0x23f, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x4f3, 0xfff, 0xfff, 0x3c7,
+	0xfff, 0x278, 0xfff, 0xfff, 0x0a6, 0xfff, 0xfff, 0xfff,
+	0x122, 0x1cf, 0xfff, 0x327, 0xfff, 0x2e5, 0xfff, 0x29d,
+	0xfff, 0xfff, 0x3f1, 0xfff, 0xfff, 0x48d, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x054, 0xfff, 0xfff, 0xfff, 0xfff, 0x178,
+	0x27e, 0x4e0, 0x352, 0x02f, 0x09c, 0xfff, 0x2a0, 0xfff,
+	0xfff, 0x46a, 0x457, 0xfff, 0xfff, 0x501, 0xfff, 0x2ba,
+	0xfff, 0xfff, 0xfff, 0x54e, 0x2e7, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x551, 0xfff, 0xfff, 0x1db, 0x2aa, 0xfff,
+	0xfff, 0x4bc, 0xfff, 0xfff, 0x395, 0xfff, 0x0de, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x455, 0xfff, 0x17e,
+	0xfff, 0x221, 0x4a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x388, 0xfff, 0xfff, 0xfff, 0x308, 0xfff, 0xfff, 0xfff,
+	0x20e, 0x4b9, 0xfff, 0x273, 0x20c, 0x09e, 0xfff, 0x057,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x3f2, 0xfff, 0x1a8, 0x3a6,
+	0x14c, 0xfff, 0xfff, 0x071, 0xfff, 0xfff, 0x53a, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x109, 0xfff, 0xfff, 0x399, 0xfff,
+	0x061, 0x4f0, 0x39e, 0x244, 0xfff, 0x035, 0xfff, 0xfff,
+	0x305, 0x47e, 0x297, 0xfff, 0xfff, 0x2b8, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1bc, 0xfff, 0x2fc,
+	0xfff, 0xfff, 0x554, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b6,
+	0xfff, 0xfff, 0xfff, 0x515, 0x397, 0xfff, 0xfff, 0x12f,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e5,
+	0xfff, 0x4fc, 0xfff, 0xfff, 0x05e, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x0a8, 0x3af, 0x015, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x138, 0xfff, 0xfff, 0xfff, 0x540, 0xfff, 0xfff,
+	0xfff, 0x027, 0x523, 0x2f0, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x16c, 0xfff, 0x27d, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x04c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4dc,
+	0xfff, 0xfff, 0x059, 0x301, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x1a3, 0xfff, 0x15a, 0xfff, 0xfff,
+	0x0a5, 0xfff, 0x435, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x051, 0xfff, 0xfff, 0x131, 0xfff, 0x4f4, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x441, 0xfff, 0x4fb, 0xfff, 0x03b,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ed, 0x274,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d3, 0x55e, 0x1b3,
+	0xfff, 0x0bd, 0xfff, 0xfff, 0xfff, 0xfff, 0x225, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x4b7, 0xfff, 0xfff, 0x2ff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c3, 0xfff,
+	0x383, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f6,
+	0xfff, 0xfff, 0x1ee, 0xfff, 0x03d, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x26f, 0x1dc, 0xfff, 0x0db, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x0ce, 0xfff, 0xfff, 0x127, 0x03a,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x311, 0xfff,
+	0xfff, 0x13d, 0x09d, 0x47b, 0x2a6, 0x50d, 0x510, 0x19a,
+	0xfff, 0x354, 0x414, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x44c, 0x3b0, 0xfff, 0x23d, 0x429, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x4c0, 0x416, 0xfff, 0x05b, 0xfff, 0xfff, 0x137, 0xfff,
+	0x25f, 0x49f, 0xfff, 0x279, 0x013, 0xfff, 0xfff, 0xfff,
+	0x269, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d0, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x077, 0xfff, 0xfff, 0x3fb,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x271, 0x3a0, 0xfff, 0xfff,
+	0x40f, 0xfff, 0xfff, 0x3de, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ab, 0x26a,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x489, 0xfff, 0xfff,
+	0x252, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b7, 0x42f, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b7,
+	0xfff, 0x2bb, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x0f7, 0x01d, 0xfff, 0x067, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x4e2, 0xfff, 0xfff, 0x4bb, 0xfff,
+	0xfff, 0xfff, 0x17b, 0xfff, 0x0ee, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x36e, 0xfff, 0xfff, 0xfff, 0x533, 0xfff,
+	0xfff, 0xfff, 0x4d4, 0x356, 0xfff, 0xfff, 0x375, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x4a4, 0x513, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4ff, 0xfff, 0x2af,
+	0xfff, 0xfff, 0x026, 0xfff, 0x0ad, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x26e, 0xfff, 0xfff, 0xfff, 0xfff, 0x493, 0xfff,
+	0x463, 0x4d2, 0x4be, 0xfff, 0xfff, 0xfff, 0xfff, 0x4f2,
+	0x0b6, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x32d, 0x315, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x13a, 0x4a1, 0xfff, 0x27a, 0xfff, 0xfff, 0xfff,
+	0x47a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x334, 0xfff, 0xfff, 0xfff, 0xfff, 0x54c, 0xfff, 0xfff,
+	0xfff, 0x0c9, 0x007, 0xfff, 0xfff, 0x12e, 0xfff, 0x0ff,
+	0xfff, 0xfff, 0x3f5, 0x509, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x1c3, 0x2ad, 0xfff, 0xfff, 0x47c, 0x261, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x152, 0xfff, 0xfff, 0xfff, 0x339,
+	0xfff, 0x243, 0x1c0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x063, 0xfff, 0xfff, 0x254, 0xfff, 0xfff, 0x173, 0xfff,
+	0x0c7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x362, 0x259, 0x485, 0x374, 0x0dc, 0x3ab, 0xfff,
+	0x1c5, 0x534, 0x544, 0xfff, 0xfff, 0x508, 0xfff, 0x402,
+	0x408, 0xfff, 0x0e7, 0xfff, 0xfff, 0x00a, 0x205, 0xfff,
+	0xfff, 0x2b9, 0xfff, 0xfff, 0xfff, 0x465, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x23a, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x147, 0x19d, 0x115, 0x214, 0xfff, 0x090, 0x368,
+	0xfff, 0x210, 0xfff, 0xfff, 0x280, 0x52a, 0x163, 0x148,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x326, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x2de, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x206, 0x2c1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x189, 0xfff, 0xfff, 0xfff, 0xfff, 0x367, 0xfff, 0x1a4,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x443, 0xfff, 0x27b,
+	0xfff, 0xfff, 0x251, 0x549, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x188, 0x04b, 0xfff, 0xfff, 0xfff, 0x31f,
+	0x4a6, 0xfff, 0x246, 0x1de, 0x156, 0xfff, 0xfff, 0xfff,
+	0x3a9, 0xfff, 0xfff, 0xfff, 0x2fa, 0xfff, 0x128, 0x0d1,
+	0x449, 0x255, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x258, 0xfff, 0xfff, 0xfff,
+	0x532, 0xfff, 0xfff, 0xfff, 0x303, 0x517, 0xfff, 0xfff,
+	0x2a9, 0x24a, 0xfff, 0xfff, 0x231, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x4b6, 0x516, 0xfff, 0xfff, 0x0e4, 0x0eb,
+	0xfff, 0x4e4, 0xfff, 0x275, 0xfff, 0xfff, 0x031, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x025, 0x21a, 0xfff, 0x0cc,
+	0x45f, 0x3d9, 0x289, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x23e, 0xfff, 0xfff, 0xfff, 0x438, 0x097,
+	0x419, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x0a9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x37e, 0x0e0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x431,
+	0x372, 0xfff, 0xfff, 0xfff, 0x1ba, 0x06e, 0xfff, 0x1b1,
+	0xfff, 0xfff, 0x12a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x193, 0xfff, 0xfff, 0xfff, 0xfff, 0x10a,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x048, 0x1b4,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x295, 0x140, 0x108, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x16f, 0xfff, 0x0a4, 0x37a, 0xfff,
+	0x29a, 0xfff, 0x284, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c6,
+	0x2a2, 0x3a3, 0xfff, 0x201, 0xfff, 0xfff, 0xfff, 0x4bd,
+	0x005, 0x54a, 0x3b5, 0x204, 0x2ee, 0x11d, 0x436, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x3ec, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x11f, 0x498, 0x21c, 0xfff,
+	0xfff, 0xfff, 0x3d6, 0xfff, 0x4ab, 0xfff, 0x432, 0x2eb,
+	0x542, 0x4fd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x4ce, 0xfff, 0xfff, 0x2fb, 0xfff,
+	0xfff, 0x2e1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b9, 0x037, 0x0dd,
+	0xfff, 0xfff, 0xfff, 0x2bf, 0x521, 0x496, 0x095, 0xfff,
+	0xfff, 0x328, 0x070, 0x1bf, 0xfff, 0x393, 0xfff, 0xfff,
+	0x102, 0xfff, 0xfff, 0x21b, 0xfff, 0x142, 0x263, 0x519,
+	0xfff, 0x2a5, 0x177, 0xfff, 0x14d, 0x471, 0x4ae, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x1f6, 0xfff, 0x481, 0xfff, 0xfff, 0xfff, 0x151, 0xfff,
+	0xfff, 0xfff, 0x085, 0x33f, 0xfff, 0xfff, 0xfff, 0x084,
+	0xfff, 0xfff, 0xfff, 0x345, 0x3a2, 0xfff, 0xfff, 0x0a0,
+	0x0da, 0x024, 0xfff, 0xfff, 0xfff, 0x1bd, 0xfff, 0x55c,
+	0x467, 0x445, 0xfff, 0xfff, 0xfff, 0x052, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x51e, 0xfff, 0xfff, 0x39d, 0xfff, 0x35f,
+	0xfff, 0x376, 0x3ee, 0xfff, 0xfff, 0xfff, 0xfff, 0x448,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x16a,
+	0xfff, 0x036, 0x38f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x211,
+	0xfff, 0xfff, 0xfff, 0x230, 0xfff, 0xfff, 0x3ba, 0xfff,
+	0xfff, 0xfff, 0x3ce, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x229, 0xfff, 0x176, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x00b, 0xfff, 0x162, 0x018, 0xfff,
+	0xfff, 0x233, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x400, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x12b, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x3f4, 0xfff, 0x0f0, 0xfff, 0x1ac, 0xfff, 0xfff,
+	0x119, 0xfff, 0x2c0, 0xfff, 0xfff, 0xfff, 0x49b, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x23c, 0xfff,
+	0x4b3, 0x010, 0x064, 0xfff, 0xfff, 0x4ba, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x3c2, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x006, 0x196, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x100, 0x191, 0xfff,
+	0x1ea, 0x29f, 0xfff, 0xfff, 0xfff, 0x276, 0xfff, 0xfff,
+	0x2b1, 0x3b9, 0xfff, 0x03c, 0xfff, 0xfff, 0xfff, 0x180,
+	0xfff, 0x08f, 0xfff, 0xfff, 0x19e, 0x019, 0xfff, 0x0b0,
+	0x0fd, 0x332, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x06b, 0x2e8, 0xfff, 0x446, 0xfff, 0xfff, 0x004,
+	0x247, 0x197, 0xfff, 0x112, 0x169, 0x292, 0xfff, 0x302,
+	0xfff, 0xfff, 0x33b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x287, 0x21f, 0xfff, 0x3ea, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e7, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x3a8, 0xfff, 0xfff, 0x2bc, 0xfff,
+	0x484, 0x296, 0xfff, 0x1c9, 0x08c, 0x1e5, 0x48a, 0xfff,
+	0x360, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x1ca, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x10d, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x066, 0x2ea, 0x28b, 0x25b, 0xfff, 0x072,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x2b6, 0xfff, 0xfff, 0x272,
+	0xfff, 0xfff, 0x525, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x2ca, 0xfff, 0xfff, 0xfff, 0x299, 0xfff, 0xfff, 0xfff,
+	0x558, 0x41a, 0xfff, 0x4f7, 0x557, 0xfff, 0x4a0, 0x344,
+	0x12c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x125,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x40e, 0xfff, 0xfff, 0x502, 0xfff, 0x103, 0x3e6, 0xfff,
+	0x527, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x45d, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x44e, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d2, 0x4c9, 0x35e,
+	0x459, 0x2d9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x17d,
+	0x0c4, 0xfff, 0xfff, 0xfff, 0x3ac, 0x390, 0x094, 0xfff,
+	0x483, 0x0ab, 0xfff, 0x253, 0xfff, 0x391, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x123, 0x0ef, 0xfff, 0xfff, 0xfff, 0x330,
+	0x38c, 0xfff, 0xfff, 0x2ae, 0xfff, 0xfff, 0xfff, 0x042,
+	0x012, 0x06d, 0xfff, 0xfff, 0xfff, 0x32a, 0x3db, 0x364,
+	0x2dc, 0xfff, 0x30f, 0x3d7, 0x4a5, 0x050, 0xfff, 0xfff,
+	0x029, 0xfff, 0xfff, 0xfff, 0xfff, 0x1d1, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x480, 0xfff,
+	0x4ed, 0x081, 0x0a1, 0xfff, 0xfff, 0xfff, 0x30e, 0x52f,
+	0x257, 0xfff, 0xfff, 0x447, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x401, 0x3cc, 0xfff, 0xfff, 0x0fb,
+	0x2c9, 0x42a, 0x314, 0x33e, 0x3bd, 0x318, 0xfff, 0x10e,
+	0x2a1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x24c,
+	0x506, 0xfff, 0x267, 0xfff, 0xfff, 0x219, 0xfff, 0x1eb,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x309, 0x3e2, 0x46c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x384, 0xfff, 0xfff, 0xfff, 0xfff, 0x50c, 0xfff, 0x24b,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x038,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x194,
+	0x143, 0x3e3, 0xfff, 0xfff, 0xfff, 0x4c2, 0xfff, 0xfff,
+	0x0e1, 0x25c, 0xfff, 0x237, 0xfff, 0x1fe, 0xfff, 0xfff,
+	0xfff, 0x065, 0x2a4, 0xfff, 0x386, 0x55a, 0x11b, 0xfff,
+	0xfff, 0x192, 0xfff, 0x183, 0x00e, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x4b2, 0x18e, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x486, 0x4ef, 0x0c6, 0x380, 0xfff, 0x4a8, 0xfff,
+	0x0c5, 0xfff, 0xfff, 0xfff, 0xfff, 0x093, 0x1b8, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e6,
+	0xfff, 0x0f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x28e, 0xfff, 0x53b, 0x420, 0x22a, 0x33a, 0xfff, 0x387,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2a3, 0xfff, 0xfff,
+	0xfff, 0x428, 0x500, 0xfff, 0xfff, 0x120, 0x2c6, 0x290,
+	0x2f5, 0x0e3, 0xfff, 0x0b7, 0xfff, 0x319, 0x474, 0xfff,
+	0xfff, 0xfff, 0x529, 0x014, 0xfff, 0x41b, 0x40a, 0x18b,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d9,
+	0xfff, 0x38a, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ce, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x3b1, 0xfff, 0xfff, 0x05d,
+	0x2c4, 0xfff, 0xfff, 0x4af, 0xfff, 0x030, 0xfff, 0xfff,
+	0x203, 0xfff, 0x277, 0x256, 0xfff, 0xfff, 0xfff, 0x4f9,
+	0xfff, 0x2c7, 0xfff, 0x466, 0x016, 0x1cd, 0xfff, 0x167,
+	0xfff, 0xfff, 0x0c8, 0xfff, 0x43d, 0xfff, 0xfff, 0x020,
+	0xfff, 0xfff, 0x232, 0x1cb, 0x1e0, 0xfff, 0xfff, 0x347,
+	0xfff, 0x478, 0xfff, 0x365, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x358, 0xfff, 0x10b, 0xfff, 0x35d, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x452, 0x22d, 0xfff, 0xfff, 0x47d, 0xfff,
+	0x2f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x460, 0xfff,
+	0xfff, 0xfff, 0x50b, 0xfff, 0xfff, 0xfff, 0x2ec, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x4b1, 0x422, 0xfff, 0xfff,
+	0xfff, 0x2d4, 0xfff, 0x239, 0xfff, 0xfff, 0xfff, 0x439,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x491, 0x075, 0xfff, 0xfff, 0xfff, 0x06c, 0xfff,
+	0xfff, 0x0f9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x139, 0xfff, 0x4f6, 0xfff, 0xfff, 0x409, 0xfff,
+	0xfff, 0x15b, 0xfff, 0xfff, 0x348, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x4a2, 0x49d, 0xfff, 0x033, 0x175, 0xfff, 0x039,
+	0xfff, 0x312, 0x40c, 0xfff, 0xfff, 0x325, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x4aa, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0x165, 0x3bc, 0x48c, 0x310, 0x096,
+	0xfff, 0xfff, 0x250, 0x1a2, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x20d, 0x2ac, 0xfff, 0xfff, 0x39b, 0xfff, 0x377, 0xfff,
+	0x512, 0x495, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x357, 0x4ea, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x198, 0xfff, 0xfff, 0xfff, 0x434, 0x04a,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x062, 0xfff, 0x1d6, 0x1c8,
+	0xfff, 0x1f3, 0x281, 0xfff, 0x462, 0xfff, 0xfff, 0xfff,
+	0x4b0, 0xfff, 0x207, 0xfff, 0xfff, 0xfff, 0xfff, 0x3dd,
+	0xfff, 0xfff, 0x55d, 0xfff, 0x552, 0x494, 0x1af, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x227, 0xfff, 0xfff, 0x069,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x43e,
+	0x0b5, 0xfff, 0x524, 0x2d2, 0xfff, 0xfff, 0xfff, 0x28f,
+	0xfff, 0x01b, 0x50e, 0xfff, 0xfff, 0x1bb, 0xfff, 0xfff,
+	0x41d, 0xfff, 0x32e, 0x48e, 0xfff, 0x1f7, 0x224, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x394, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x52c, 0xfff, 0xfff, 0xfff, 0x392, 0xfff, 0x1e7,
+	0xfff, 0xfff, 0x3f9, 0x3a7, 0xfff, 0x51f, 0xfff, 0x0bb,
+	0x118, 0x3ca, 0xfff, 0x1dd, 0xfff, 0x48b, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x50f, 0xfff, 0x0d6, 0xfff, 0x1fa, 0xfff,
+	0x11e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d7, 0xfff, 0x078,
+	0x008, 0xfff, 0x25d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x032, 0x33c, 0xfff, 0x4d9, 0x160, 0xfff, 0xfff, 0x300,
+	0x0b1, 0xfff, 0x322, 0xfff, 0x4ec, 0xfff, 0xfff, 0x200,
+	0x00c, 0x369, 0x473, 0xfff, 0xfff, 0x32c, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x53e, 0x3d4, 0x417, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x34b, 0x001, 0x39a, 0x02c, 0xfff, 0xfff, 0x2ce, 0x00f,
+	0xfff, 0x0ba, 0xfff, 0xfff, 0xfff, 0xfff, 0x060, 0xfff,
+	0x406, 0xfff, 0xfff, 0xfff, 0x4ee, 0x4ac, 0xfff, 0x43f,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x29b, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x216,
+	0x190, 0xfff, 0x396, 0x464, 0xfff, 0xfff, 0x323, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e9, 0xfff, 0x26d,
+	0x2cd, 0x040, 0xfff, 0xfff, 0xfff, 0xfff, 0x38b, 0x3c0,
+	0xfff, 0xfff, 0xfff, 0x1f2, 0xfff, 0x0ea, 0xfff, 0xfff,
+	0x472, 0xfff, 0x1fb, 0xfff, 0xfff, 0x0af, 0x27f, 0xfff,
+	0xfff, 0xfff, 0x479, 0x023, 0xfff, 0x0d8, 0x3b3, 0xfff,
+	0xfff, 0xfff, 0x121, 0xfff, 0xfff, 0x3bf, 0xfff, 0xfff,
+	0x16b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x45a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x0be, 0xfff, 0xfff, 0xfff, 0x111, 0xfff, 0x220,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x09b, 0x218, 0xfff, 0x022, 0x202, 0xfff,
+	0x4c8, 0xfff, 0x0ed, 0xfff, 0xfff, 0x182, 0xfff, 0xfff,
+	0xfff, 0x17f, 0x213, 0xfff, 0x321, 0x36a, 0xfff, 0x086,
+	0xfff, 0xfff, 0xfff, 0x43b, 0x088, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x26c, 0xfff, 0x2f8, 0x3b4, 0xfff, 0xfff, 0xfff,
+	0x132, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x333, 0x444,
+	0x0c1, 0x4d8, 0x46d, 0x264, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x426, 0xfff, 0xfff, 0xfff, 0xfff, 0x2fe, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x011, 0xfff, 0x05f, 0xfff, 0xfff, 0xfff,
+	0xfff, 0x10c, 0x101, 0xfff, 0xfff, 0xfff, 0xfff, 0x110,
+	0xfff, 0x044, 0x304, 0x361, 0x404, 0xfff, 0x51b, 0x099,
+	0xfff, 0x440, 0xfff, 0xfff, 0xfff, 0x222, 0xfff, 0xfff,
+	0xfff, 0xfff, 0x1b5, 0xfff, 0x136, 0x430, 0xfff, 0x1da,
+	0xfff, 0xfff, 0xfff, 0x043, 0xfff, 0x17c, 0xfff, 0xfff,
+	0xfff, 0x01c, 0xfff, 0xfff, 0xfff, 0x425, 0x236, 0xfff,
+	0x317, 0xfff, 0xfff, 0x437, 0x3fc, 0xfff, 0x1f1, 0xfff,
+	0x324, 0xfff, 0xfff, 0x0ca, 0x306, 0xfff, 0x548, 0xfff,
+	0x46e, 0xfff, 0xfff, 0xfff, 0x4b8, 0x1c2, 0x286, 0xfff,
+	0xfff, 0x087, 0x18a, 0x19f, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x18c, 0xfff, 0x215, 0xfff, 0xfff, 0xfff, 0xfff, 0x283,
+	0xfff, 0xfff, 0xfff, 0x126, 0xfff, 0xfff, 0x370, 0xfff,
+	0x53f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x31c, 0xfff,
+	0x4d1, 0xfff, 0xfff, 0xfff, 0x021, 0xfff, 0x157, 0xfff,
+	0xfff, 0x028, 0x16e, 0xfff, 0x421, 0xfff, 0x1c6, 0xfff,
+	0xfff, 0x511, 0xfff, 0xfff, 0x39c, 0x46f, 0x1b2, 0xfff,
+	0xfff, 0x316, 0xfff, 0xfff, 0x009, 0xfff, 0xfff, 0x195,
+	0xfff, 0x240, 0x546, 0xfff, 0xfff, 0x520, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x454, 0xfff, 0xfff, 0xfff,
+	0x3f3, 0xfff, 0xfff, 0x187, 0xfff, 0x4a9, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x51c, 0x453, 0x1e6, 0xfff,
+	0xfff, 0xfff, 0x1b0, 0xfff, 0x477, 0xfff, 0xfff, 0xfff,
+	0x4fe, 0xfff, 0x32f, 0xfff, 0xfff, 0x15e, 0x1d4, 0xfff,
+	0x0e5, 0xfff, 0xfff, 0xfff, 0x242, 0x14b, 0x046, 0xfff,
+	0x3f6, 0x3bb, 0x3e4, 0xfff, 0xfff, 0x2e3, 0xfff, 0x245,
+	0xfff, 0x149, 0xfff, 0xfff, 0xfff, 0x2db, 0xfff, 0xfff,
+	0x181, 0xfff, 0x089, 0x2c5, 0xfff, 0x1f5, 0xfff, 0x2d6,
+	0x507, 0xfff, 0x42d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0x080, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
+	0xfff, 0xfff, 0xfff, 0xfff, 0x3c3, 0x320, 0xfff, 0x1e1,
+	0xfff, 0x0f5, 0x13b, 0xfff, 0xfff, 0xfff, 0x003, 0x4da,
+	0xfff, 0xfff, 0xfff, 0x42c, 0xfff, 0xfff, 0x0cb, 0xfff,
+	0x536, 0x2c3, 0xfff, 0xfff, 0xfff, 0xfff, 0x199, 0xfff,
+	0xfff, 0x0c0, 0xfff, 0x01e, 0x497, 0xfff, 0xfff, 0x3e5,
+	0xfff, 0xfff, 0xfff, 0x0cf, 0xfff, 0x2bd, 0xfff, 0x223,
+	0xfff, 0x3ff, 0xfff, 0x058, 0x174, 0x3ef, 0xfff, 0x002
+};
+
+static unsigned short err_pos(unsigned short din)
+{
+	BUG_ON(din > 4096);
+	return err_pos_lut[din];
+}
+static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
+{
+	if ((chk_syndrome_list[0] | chk_syndrome_list[1] |
+	     chk_syndrome_list[2] | chk_syndrome_list[3] |
+	     chk_syndrome_list[4] | chk_syndrome_list[5] |
+	     chk_syndrome_list[6] | chk_syndrome_list[7]) != 0x0) {
+		return -EINVAL;
+	} else {
+		err_info[0] = 0x0;
+		return 0;
+	}
+}
+static int chk_1_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
+{
+	unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
+	tmp0 = gf4096_mul(chk_syndrome_list[1], gf4096_inv(chk_syndrome_list[0]));
+	tmp1 = gf4096_mul(chk_syndrome_list[2], gf4096_inv(chk_syndrome_list[1]));
+	tmp2 = gf4096_mul(chk_syndrome_list[3], gf4096_inv(chk_syndrome_list[2]));
+	tmp3 = gf4096_mul(chk_syndrome_list[4], gf4096_inv(chk_syndrome_list[3]));
+	tmp4 = gf4096_mul(chk_syndrome_list[5], gf4096_inv(chk_syndrome_list[4]));
+	tmp5 = gf4096_mul(chk_syndrome_list[6], gf4096_inv(chk_syndrome_list[5]));
+	tmp6 = gf4096_mul(chk_syndrome_list[7], gf4096_inv(chk_syndrome_list[6]));
+	if ((tmp0 == tmp1) & (tmp1 == tmp2) & (tmp2 == tmp3) & (tmp3 == tmp4) & (tmp4 == tmp5) & (tmp5 == tmp6)) {
+		err_info[0] = 0x1;	// encode 1-symbol error as 0x1
+		err_info[1] = err_pos(tmp0);
+		err_info[1] = (unsigned short)(0x55e - err_info[1]);
+		err_info[5] = chk_syndrome_list[0];
+		return 0;
+	} else
+		return -EINVAL;
+}
+static int chk_2_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
+{
+	unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+	unsigned short coefs[4];
+	unsigned short err_pats[4];
+	int found_num_root = 0;
+	unsigned short bit2_root0, bit2_root1;
+	unsigned short bit2_root0_inv, bit2_root1_inv;
+	unsigned short err_loc_eqn, test_root;
+	unsigned short bit2_loc0, bit2_loc1;
+	unsigned short bit2_pat0, bit2_pat1;
+
+	find_2x2_soln(chk_syndrome_list[1],
+		      chk_syndrome_list[0],
+		      chk_syndrome_list[2], chk_syndrome_list[1], chk_syndrome_list[2], chk_syndrome_list[3], coefs);
+	for (test_root = 0x1; test_root < 0xfff; test_root++) {
+		err_loc_eqn =
+		    gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) ^ gf4096_mul(coefs[0], test_root) ^ 0x1;
+		if (err_loc_eqn == 0x0) {
+			if (found_num_root == 0) {
+				bit2_root0 = test_root;
+				found_num_root = 1;
+			} else if (found_num_root == 1) {
+				bit2_root1 = test_root;
+				found_num_root = 2;
+				break;
+			}
+		}
+	}
+	if (found_num_root != 2)
+		return -EINVAL;
+	else {
+		bit2_root0_inv = gf4096_inv(bit2_root0);
+		bit2_root1_inv = gf4096_inv(bit2_root1);
+		find_2bit_err_pats(chk_syndrome_list[0],
+				   chk_syndrome_list[1], bit2_root0_inv, bit2_root1_inv, err_pats);
+		bit2_pat0 = err_pats[0];
+		bit2_pat1 = err_pats[1];
+		//for(x+1)
+		tmp0 = gf4096_mul(gf4096_mul(bit2_root0_inv, bit2_root0_inv), gf4096_mul(bit2_root0_inv, bit2_root0_inv));	//rinv0^4
+		tmp1 = gf4096_mul(bit2_root0_inv, tmp0);	//rinv0^5
+		tmp2 = gf4096_mul(bit2_root0_inv, tmp1);	//rinv0^6
+		tmp3 = gf4096_mul(bit2_root0_inv, tmp2);	//rinv0^7
+		tmp4 = gf4096_mul(gf4096_mul(bit2_root1_inv, bit2_root1_inv), gf4096_mul(bit2_root1_inv, bit2_root1_inv));	//rinv1^4
+		tmp5 = gf4096_mul(bit2_root1_inv, tmp4);	//rinv1^5
+		tmp6 = gf4096_mul(bit2_root1_inv, tmp5);	//rinv1^6
+		tmp7 = gf4096_mul(bit2_root1_inv, tmp6);	//rinv1^7
+		//check if only 2-bit error
+		if ((chk_syndrome_list[4] ==
+		     (gf4096_mul(bit2_pat0, tmp0) ^
+		      gf4096_mul(bit2_pat1,
+				 tmp4))) & (chk_syndrome_list[5] ==
+					    (gf4096_mul(bit2_pat0, tmp1) ^
+					     gf4096_mul(bit2_pat1,
+							tmp5))) &
+		    (chk_syndrome_list[6] ==
+		     (gf4096_mul(bit2_pat0, tmp2) ^
+		      gf4096_mul(bit2_pat1,
+				 tmp6))) & (chk_syndrome_list[7] ==
+					    (gf4096_mul(bit2_pat0, tmp3) ^ gf4096_mul(bit2_pat1, tmp7)))) {
+			if ((err_pos(bit2_root0_inv) == 0xfff) | (err_pos(bit2_root1_inv) == 0xfff)) {
+				return -EINVAL;
+			} else {
+				bit2_loc0 = 0x55e - err_pos(bit2_root0_inv);
+				bit2_loc1 = 0x55e - err_pos(bit2_root1_inv);
+				err_info[0] = 0x2;	// encode 2-symbol error as 0x2
+				err_info[1] = bit2_loc0;
+				err_info[2] = bit2_loc1;
+				err_info[5] = bit2_pat0;
+				err_info[6] = bit2_pat1;
+				return 0;
+			}
+		} else
+			return -EINVAL;
+	}
+}
+static int chk_3_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
+{
+	unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5;
+	unsigned short coefs[4];
+	unsigned short err_pats[4];
+	int found_num_root = 0;
+	unsigned short bit3_root0, bit3_root1, bit3_root2;
+	unsigned short bit3_root0_inv, bit3_root1_inv, bit3_root2_inv;
+	unsigned short err_loc_eqn, test_root;
+
+	find_3bit_err_coefs(chk_syndrome_list[0], chk_syndrome_list[1],
+			    chk_syndrome_list[2], chk_syndrome_list[3],
+			    chk_syndrome_list[4], chk_syndrome_list[5], coefs);
+
+	for (test_root = 0x1; test_root < 0xfff; test_root++) {
+		err_loc_eqn = gf4096_mul(coefs[2],
+					 gf4096_mul(gf4096_mul(test_root, test_root),
+						    test_root)) ^ gf4096_mul(coefs[1], gf4096_mul(test_root, test_root))
+			^ gf4096_mul(coefs[0], test_root) ^ 0x1;
+
+		if (err_loc_eqn == 0x0) {
+			if (found_num_root == 0) {
+				bit3_root0 = test_root;
+				found_num_root = 1;
+			} else if (found_num_root == 1) {
+				bit3_root1 = test_root;
+				found_num_root = 2;
+			} else if (found_num_root == 2) {
+				bit3_root2 = test_root;
+				found_num_root = 3;
+				break;
+			}
+		}
+	}
+	if (found_num_root != 3)
+		return -EINVAL;
+	else {
+		bit3_root0_inv = gf4096_inv(bit3_root0);
+		bit3_root1_inv = gf4096_inv(bit3_root1);
+		bit3_root2_inv = gf4096_inv(bit3_root2);
+
+		find_3bit_err_pats(chk_syndrome_list[0], chk_syndrome_list[1],
+				   chk_syndrome_list[2], bit3_root0_inv,
+				   bit3_root1_inv, bit3_root2_inv, err_pats);
+
+		//check if only 3-bit error
+		tmp0 = gf4096_mul(bit3_root0_inv, bit3_root0_inv);
+		tmp0 = gf4096_mul(tmp0, tmp0);
+		tmp0 = gf4096_mul(tmp0, bit3_root0_inv);
+		tmp0 = gf4096_mul(tmp0, bit3_root0_inv);	//rinv0^6
+		tmp1 = gf4096_mul(tmp0, bit3_root0_inv);	//rinv0^7
+		tmp2 = gf4096_mul(bit3_root1_inv, bit3_root1_inv);
+		tmp2 = gf4096_mul(tmp2, tmp2);
+		tmp2 = gf4096_mul(tmp2, bit3_root1_inv);
+		tmp2 = gf4096_mul(tmp2, bit3_root1_inv);	//rinv1^6
+		tmp3 = gf4096_mul(tmp2, bit3_root1_inv);	//rinv1^7
+		tmp4 = gf4096_mul(bit3_root2_inv, bit3_root2_inv);
+		tmp4 = gf4096_mul(tmp4, tmp4);
+		tmp4 = gf4096_mul(tmp4, bit3_root2_inv);
+		tmp4 = gf4096_mul(tmp4, bit3_root2_inv);	//rinv2^6
+		tmp5 = gf4096_mul(tmp4, bit3_root2_inv);	//rinv2^7
+
+		//check if only 3 errors
+		if ((chk_syndrome_list[6] == (gf4096_mul(err_pats[0], tmp0) ^
+					      gf4096_mul(err_pats[1], tmp2) ^
+					      gf4096_mul(err_pats[2], tmp4))) &
+		    (chk_syndrome_list[7] == (gf4096_mul(err_pats[0], tmp1) ^
+					      gf4096_mul(err_pats[1], tmp3) ^ gf4096_mul(err_pats[2], tmp5)))) {
+			if ((err_pos(bit3_root0_inv) == 0xfff) |
+			    (err_pos(bit3_root1_inv) == 0xfff) | (err_pos(bit3_root2_inv) == 0xfff)) {
+				return -EINVAL;
+			} else {
+				err_info[0] = 0x3;
+				err_info[1] = (0x55e - err_pos(bit3_root0_inv));
+				err_info[2] = (0x55e - err_pos(bit3_root1_inv));
+				err_info[3] = (0x55e - err_pos(bit3_root2_inv));
+				err_info[5] = err_pats[0];
+				err_info[6] = err_pats[1];
+				err_info[7] = err_pats[2];
+				return 0;
+			}
+		} else
+			return -EINVAL;
+	}
+}
+static int chk_4_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
+{
+	unsigned short coefs[4];
+	unsigned short err_pats[4];
+	int found_num_root = 0;
+	unsigned short bit4_root0, bit4_root1, bit4_root2, bit4_root3;
+	unsigned short bit4_root0_inv, bit4_root1_inv, bit4_root2_inv, bit4_root3_inv;
+	unsigned short err_loc_eqn, test_root;
+
+	find_4bit_err_coefs(chk_syndrome_list[0],
+			    chk_syndrome_list[1],
+			    chk_syndrome_list[2],
+			    chk_syndrome_list[3],
+			    chk_syndrome_list[4],
+			    chk_syndrome_list[5], chk_syndrome_list[6], chk_syndrome_list[7], coefs);
+
+	for (test_root = 0x1; test_root < 0xfff; test_root++) {
+		err_loc_eqn =
+		    gf4096_mul(coefs[3],
+			       gf4096_mul(gf4096_mul
+					  (gf4096_mul(test_root, test_root),
+					   test_root),
+					  test_root)) ^ gf4096_mul(coefs[2],
+								   gf4096_mul
+								   (gf4096_mul(test_root, test_root), test_root))
+		    ^ gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) ^ gf4096_mul(coefs[0], test_root)
+		    ^ 0x1;
+		if (err_loc_eqn == 0x0) {
+			if (found_num_root == 0) {
+				bit4_root0 = test_root;
+				found_num_root = 1;
+			} else if (found_num_root == 1) {
+				bit4_root1 = test_root;
+				found_num_root = 2;
+			} else if (found_num_root == 2) {
+				bit4_root2 = test_root;
+				found_num_root = 3;
+			} else {
+				found_num_root = 4;
+				bit4_root3 = test_root;
+				break;
+			}
+		}
+	}
+	if (found_num_root != 4) {
+		return -EINVAL;
+	} else {
+		bit4_root0_inv = gf4096_inv(bit4_root0);
+		bit4_root1_inv = gf4096_inv(bit4_root1);
+		bit4_root2_inv = gf4096_inv(bit4_root2);
+		bit4_root3_inv = gf4096_inv(bit4_root3);
+		find_4bit_err_pats(chk_syndrome_list[0],
+				   chk_syndrome_list[1],
+				   chk_syndrome_list[2],
+				   chk_syndrome_list[3],
+				   bit4_root0_inv, bit4_root1_inv, bit4_root2_inv, bit4_root3_inv, err_pats);
+		err_info[0] = 0x4;
+		err_info[1] = (0x55e - err_pos(bit4_root0_inv));
+		err_info[2] = (0x55e - err_pos(bit4_root1_inv));
+		err_info[3] = (0x55e - err_pos(bit4_root2_inv));
+		err_info[4] = (0x55e - err_pos(bit4_root3_inv));
+		err_info[5] = err_pats[0];
+		err_info[6] = err_pats[1];
+		err_info[7] = err_pats[2];
+		err_info[8] = err_pats[3];
+		return 0;
+	}
+}
+
+void correct_12bit_symbol(unsigned char *buf, unsigned short sym,
+			  unsigned short val)
+{
+	if (unlikely(sym > 1366)) {
+		printk(KERN_ERR "Error: symbol %d out of range; cannot correct\n", sym);
+	} else if (sym == 0) {
+		buf[0] ^= val;
+	} else if (sym & 1) {
+		buf[1+(3*(sym-1))/2] ^= (val >> 4);
+		buf[2+(3*(sym-1))/2] ^= ((val & 0xf) << 4);
+	} else {
+		buf[2+(3*(sym-2))/2] ^= (val >> 8);
+		buf[3+(3*(sym-2))/2] ^= (val & 0xff);
+	}
+}
+
+static int debugecc = 0;
+module_param(debugecc, int, 0644);
+
+int cafe_correct_ecc(unsigned char *buf,
+		     unsigned short *chk_syndrome_list)
+{
+	unsigned short err_info[9];
+	int i;
+
+	if (debugecc) {
+		printk(KERN_WARNING "cafe_correct_ecc invoked. Syndromes %x %x %x %x %x %x %x %x\n",
+		       chk_syndrome_list[0], chk_syndrome_list[1],
+		       chk_syndrome_list[2], chk_syndrome_list[3],
+		       chk_syndrome_list[4], chk_syndrome_list[5],
+		       chk_syndrome_list[6], chk_syndrome_list[7]);
+		for (i=0; i < 2048; i+=16) {
+			printk(KERN_WARNING "D %04x: %02x %02x %02x %02x %02x %02x %02x %02x  %02x %02x %02x %02x %02x %02x %02x %02x\n",
+			       i,
+			       buf[i], buf[i+1], buf[i+2], buf[i+3],
+			       buf[i+4], buf[i+5], buf[i+6], buf[i+7],
+			       buf[i+8], buf[i+9], buf[i+10], buf[i+11],
+			       buf[i+12], buf[i+13], buf[i+14], buf[i+15]);
+		}
+		for ( ; i < 2112; i+=16) {
+			printk(KERN_WARNING "O   %02x: %02x %02x %02x %02x %02x %02x %02x %02x  %02x %02x %02x %02x %02x %02x %02x %02x\n",
+			       i - 2048,
+			       buf[i], buf[i+1], buf[i+2], buf[i+3],
+			       buf[i+4], buf[i+5], buf[i+6], buf[i+7],
+			       buf[i+8], buf[i+9], buf[i+10], buf[i+11],
+			       buf[i+12], buf[i+13], buf[i+14], buf[i+15]);
+		}
+	}
+
+
+
+	if (chk_no_err_only(chk_syndrome_list, err_info) &&
+	    chk_1_err_only(chk_syndrome_list, err_info) &&
+	    chk_2_err_only(chk_syndrome_list, err_info) &&
+	    chk_3_err_only(chk_syndrome_list, err_info) &&
+	    chk_4_err_only(chk_syndrome_list, err_info)) {
+		return -EIO;
+	}
+
+	for (i=0; i < err_info[0]; i++) {
+		if (debugecc)
+			printk(KERN_WARNING "Correct symbol %d with 0x%03x\n",
+			       err_info[1+i], err_info[5+i]);
+
+		correct_12bit_symbol(buf, err_info[1+i], err_info[5+i]);
+	}
+
+	return err_info[0];
+}
+

+ 2 - 2
drivers/mtd/nand/cs553x_nand.c

@@ -11,7 +11,7 @@
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
  *
  *
  *  Overview:
  *  Overview:
- *   This is a device driver for the NAND flash controller found on 
+ *   This is a device driver for the NAND flash controller found on
  *   the AMD CS5535/CS5536 companion chipsets for the Geode processor.
  *   the AMD CS5535/CS5536 companion chipsets for the Geode processor.
  *
  *
  */
  */
@@ -303,7 +303,7 @@ static int __init cs553x_init(void)
 			err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF);
 			err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF);
 	}
 	}
 
 
-	/* Register all devices together here. This means we can easily hack it to 
+	/* Register all devices together here. This means we can easily hack it to
 	   do mtdconcat etc. if we want to. */
 	   do mtdconcat etc. if we want to. */
 	for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
 	for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
 		if (cs553x_mtd[i]) {
 		if (cs553x_mtd[i]) {

+ 1 - 2
drivers/mtd/nand/diskonchip.c

@@ -1635,13 +1635,12 @@ static int __init doc_probe(unsigned long physadr)
 
 
 	len = sizeof(struct mtd_info) +
 	len = sizeof(struct mtd_info) +
 	    sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr));
 	    sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr));
-	mtd = kmalloc(len, GFP_KERNEL);
+	mtd = kzalloc(len, GFP_KERNEL);
 	if (!mtd) {
 	if (!mtd) {
 		printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
 		printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto fail;
 		goto fail;
 	}
 	}
-	memset(mtd, 0, len);
 
 
 	nand			= (struct nand_chip *) (mtd + 1);
 	nand			= (struct nand_chip *) (mtd + 1);
 	doc			= (struct doc_priv *) (nand + 1);
 	doc			= (struct doc_priv *) (nand + 1);

+ 91 - 42
drivers/mtd/nand/nand_base.c

@@ -362,7 +362,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
 		 * access
 		 * access
 		 */
 		 */
 		ofs += mtd->oobsize;
 		ofs += mtd->oobsize;
-		chip->ops.len = 2;
+		chip->ops.len = chip->ops.ooblen = 2;
 		chip->ops.datbuf = NULL;
 		chip->ops.datbuf = NULL;
 		chip->ops.oobbuf = buf;
 		chip->ops.oobbuf = buf;
 		chip->ops.ooboffs = chip->badblockpos & ~0x01;
 		chip->ops.ooboffs = chip->badblockpos & ~0x01;
@@ -755,7 +755,7 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 }
 }
 
 
 /**
 /**
- * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
+ * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
  * @mtd:	mtd info structure
  * @mtd:	mtd info structure
  * @chip:	nand chip info structure
  * @chip:	nand chip info structure
  * @buf:	buffer to store read data
  * @buf:	buffer to store read data
@@ -795,7 +795,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
 }
 }
 
 
 /**
 /**
- * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function
+ * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
  * @mtd:	mtd info structure
  * @mtd:	mtd info structure
  * @chip:	nand chip info structure
  * @chip:	nand chip info structure
  * @buf:	buffer to store read data
  * @buf:	buffer to store read data
@@ -839,7 +839,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
 }
 }
 
 
 /**
 /**
- * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
+ * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
  * @mtd:	mtd info structure
  * @mtd:	mtd info structure
  * @chip:	nand chip info structure
  * @chip:	nand chip info structure
  * @buf:	buffer to store read data
  * @buf:	buffer to store read data
@@ -897,12 +897,11 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
  * @chip:	nand chip structure
  * @chip:	nand chip structure
  * @oob:	oob destination address
  * @oob:	oob destination address
  * @ops:	oob ops structure
  * @ops:	oob ops structure
+ * @len:	size of oob to transfer
  */
  */
 static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
 static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
-				  struct mtd_oob_ops *ops)
+				  struct mtd_oob_ops *ops, size_t len)
 {
 {
-	size_t len = ops->ooblen;
-
 	switch(ops->mode) {
 	switch(ops->mode) {
 
 
 	case MTD_OOB_PLACE:
 	case MTD_OOB_PLACE:
@@ -960,6 +959,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 	int sndcmd = 1;
 	int sndcmd = 1;
 	int ret = 0;
 	int ret = 0;
 	uint32_t readlen = ops->len;
 	uint32_t readlen = ops->len;
+	uint32_t oobreadlen = ops->ooblen;
 	uint8_t *bufpoi, *oob, *buf;
 	uint8_t *bufpoi, *oob, *buf;
 
 
 	stats = mtd->ecc_stats;
 	stats = mtd->ecc_stats;
@@ -971,7 +971,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 	page = realpage & chip->pagemask;
 	page = realpage & chip->pagemask;
 
 
 	col = (int)(from & (mtd->writesize - 1));
 	col = (int)(from & (mtd->writesize - 1));
-	chip->oob_poi = chip->buffers->oobrbuf;
 
 
 	buf = ops->datbuf;
 	buf = ops->datbuf;
 	oob = ops->oobbuf;
 	oob = ops->oobbuf;
@@ -1007,10 +1006,17 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 
 
 			if (unlikely(oob)) {
 			if (unlikely(oob)) {
 				/* Raw mode does data:oob:data:oob */
 				/* Raw mode does data:oob:data:oob */
-				if (ops->mode != MTD_OOB_RAW)
-					oob = nand_transfer_oob(chip, oob, ops);
-				else
-					buf = nand_transfer_oob(chip, buf, ops);
+				if (ops->mode != MTD_OOB_RAW) {
+					int toread = min(oobreadlen,
+						chip->ecc.layout->oobavail);
+					if (toread) {
+						oob = nand_transfer_oob(chip,
+							oob, ops, toread);
+						oobreadlen -= toread;
+					}
+				} else
+					buf = nand_transfer_oob(chip,
+						buf, ops, mtd->oobsize);
 			}
 			}
 
 
 			if (!(chip->options & NAND_NO_READRDY)) {
 			if (!(chip->options & NAND_NO_READRDY)) {
@@ -1057,6 +1063,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 	}
 	}
 
 
 	ops->retlen = ops->len - (size_t) readlen;
 	ops->retlen = ops->len - (size_t) readlen;
+	if (oob)
+		ops->oobretlen = ops->ooblen - oobreadlen;
 
 
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
@@ -1257,12 +1265,18 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 	int page, realpage, chipnr, sndcmd = 1;
 	int page, realpage, chipnr, sndcmd = 1;
 	struct nand_chip *chip = mtd->priv;
 	struct nand_chip *chip = mtd->priv;
 	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
 	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
-	int readlen = ops->len;
+	int readlen = ops->ooblen;
+	int len;
 	uint8_t *buf = ops->oobbuf;
 	uint8_t *buf = ops->oobbuf;
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
 	DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
 	      (unsigned long long)from, readlen);
 	      (unsigned long long)from, readlen);
 
 
+	if (ops->mode == MTD_OOB_RAW)
+		len = mtd->oobsize;
+	else
+		len = chip->ecc.layout->oobavail;
+
 	chipnr = (int)(from >> chip->chip_shift);
 	chipnr = (int)(from >> chip->chip_shift);
 	chip->select_chip(mtd, chipnr);
 	chip->select_chip(mtd, chipnr);
 
 
@@ -1270,11 +1284,11 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 	realpage = (int)(from >> chip->page_shift);
 	realpage = (int)(from >> chip->page_shift);
 	page = realpage & chip->pagemask;
 	page = realpage & chip->pagemask;
 
 
-	chip->oob_poi = chip->buffers->oobrbuf;
-
 	while(1) {
 	while(1) {
 		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
 		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
-		buf = nand_transfer_oob(chip, buf, ops);
+
+		len = min(len, readlen);
+		buf = nand_transfer_oob(chip, buf, ops, len);
 
 
 		if (!(chip->options & NAND_NO_READRDY)) {
 		if (!(chip->options & NAND_NO_READRDY)) {
 			/*
 			/*
@@ -1289,7 +1303,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 				nand_wait_ready(mtd);
 				nand_wait_ready(mtd);
 		}
 		}
 
 
-		readlen -= ops->ooblen;
+		readlen -= len;
 		if (!readlen)
 		if (!readlen)
 			break;
 			break;
 
 
@@ -1311,7 +1325,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 			sndcmd = 1;
 			sndcmd = 1;
 	}
 	}
 
 
-	ops->retlen = ops->len;
+	ops->oobretlen = ops->ooblen;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1332,7 +1346,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
 	ops->retlen = 0;
 	ops->retlen = 0;
 
 
 	/* Do not allow reads past end of device */
 	/* Do not allow reads past end of device */
-	if ((from + ops->len) > mtd->size) {
+	if (ops->datbuf && (from + ops->len) > mtd->size) {
 		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
 		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
 		      "Attempt read beyond end of device\n");
 		      "Attempt read beyond end of device\n");
 		return -EINVAL;
 		return -EINVAL;
@@ -1375,7 +1389,7 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 }
 }
 
 
 /**
 /**
- * nand_write_page_swecc - {REPLACABLE] software ecc based page write function
+ * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
  * @mtd:	mtd info structure
  * @mtd:	mtd info structure
  * @chip:	nand chip info structure
  * @chip:	nand chip info structure
  * @buf:	data buffer
  * @buf:	data buffer
@@ -1401,7 +1415,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
 }
 }
 
 
 /**
 /**
- * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function
+ * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
  * @mtd:	mtd info structure
  * @mtd:	mtd info structure
  * @chip:	nand chip info structure
  * @chip:	nand chip info structure
  * @buf:	data buffer
  * @buf:	data buffer
@@ -1429,7 +1443,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
 }
 }
 
 
 /**
 /**
- * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write
+ * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
  * @mtd:	mtd info structure
  * @mtd:	mtd info structure
  * @chip:	nand chip info structure
  * @chip:	nand chip info structure
  * @buf:	data buffer
  * @buf:	data buffer
@@ -1577,7 +1591,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
 	return NULL;
 	return NULL;
 }
 }
 
 
-#define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
+#define NOTALIGNED(x)	(x & (chip->subpagesize - 1)) != 0
 
 
 /**
 /**
  * nand_do_write_ops - [Internal] NAND write with ECC
  * nand_do_write_ops - [Internal] NAND write with ECC
@@ -1590,15 +1604,16 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
 static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 			     struct mtd_oob_ops *ops)
 			     struct mtd_oob_ops *ops)
 {
 {
-	int chipnr, realpage, page, blockmask;
+	int chipnr, realpage, page, blockmask, column;
 	struct nand_chip *chip = mtd->priv;
 	struct nand_chip *chip = mtd->priv;
 	uint32_t writelen = ops->len;
 	uint32_t writelen = ops->len;
 	uint8_t *oob = ops->oobbuf;
 	uint8_t *oob = ops->oobbuf;
 	uint8_t *buf = ops->datbuf;
 	uint8_t *buf = ops->datbuf;
-	int bytes = mtd->writesize;
-	int ret;
+	int ret, subpage;
 
 
 	ops->retlen = 0;
 	ops->retlen = 0;
+	if (!writelen)
+		return 0;
 
 
 	/* reject writes, which are not page aligned */
 	/* reject writes, which are not page aligned */
 	if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
 	if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
@@ -1607,8 +1622,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	if (!writelen)
-		return 0;
+	column = to & (mtd->writesize - 1);
+	subpage = column || (writelen & (mtd->writesize - 1));
+
+	if (subpage && oob)
+		return -EINVAL;
 
 
 	chipnr = (int)(to >> chip->chip_shift);
 	chipnr = (int)(to >> chip->chip_shift);
 	chip->select_chip(mtd, chipnr);
 	chip->select_chip(mtd, chipnr);
@@ -1626,15 +1644,29 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 	    (chip->pagebuf << chip->page_shift) < (to + ops->len))
 	    (chip->pagebuf << chip->page_shift) < (to + ops->len))
 		chip->pagebuf = -1;
 		chip->pagebuf = -1;
 
 
-	chip->oob_poi = chip->buffers->oobwbuf;
+	/* If we're not given explicit OOB data, let it be 0xFF */
+	if (likely(!oob))
+		memset(chip->oob_poi, 0xff, mtd->oobsize);
 
 
 	while(1) {
 	while(1) {
+		int bytes = mtd->writesize;
 		int cached = writelen > bytes && page != blockmask;
 		int cached = writelen > bytes && page != blockmask;
+		uint8_t *wbuf = buf;
+
+		/* Partial page write ? */
+		if (unlikely(column || writelen < (mtd->writesize - 1))) {
+			cached = 0;
+			bytes = min_t(int, bytes - column, (int) writelen);
+			chip->pagebuf = -1;
+			memset(chip->buffers->databuf, 0xff, mtd->writesize);
+			memcpy(&chip->buffers->databuf[column], buf, bytes);
+			wbuf = chip->buffers->databuf;
+		}
 
 
 		if (unlikely(oob))
 		if (unlikely(oob))
 			oob = nand_fill_oob(chip, oob, ops);
 			oob = nand_fill_oob(chip, oob, ops);
 
 
-		ret = chip->write_page(mtd, chip, buf, page, cached,
+		ret = chip->write_page(mtd, chip, wbuf, page, cached,
 				       (ops->mode == MTD_OOB_RAW));
 				       (ops->mode == MTD_OOB_RAW));
 		if (ret)
 		if (ret)
 			break;
 			break;
@@ -1643,6 +1675,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 		if (!writelen)
 		if (!writelen)
 			break;
 			break;
 
 
+		column = 0;
 		buf += bytes;
 		buf += bytes;
 		realpage++;
 		realpage++;
 
 
@@ -1655,10 +1688,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 		}
 		}
 	}
 	}
 
 
-	if (unlikely(oob))
-		memset(chip->oob_poi, 0xff, mtd->oobsize);
-
 	ops->retlen = ops->len - writelen;
 	ops->retlen = ops->len - writelen;
+	if (unlikely(oob))
+		ops->oobretlen = ops->ooblen;
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1714,10 +1746,10 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 	struct nand_chip *chip = mtd->priv;
 	struct nand_chip *chip = mtd->priv;
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
 	DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
-	      (unsigned int)to, (int)ops->len);
+	      (unsigned int)to, (int)ops->ooblen);
 
 
 	/* Do not allow write past end of page */
 	/* Do not allow write past end of page */
-	if ((ops->ooboffs + ops->len) > mtd->oobsize) {
+	if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) {
 		DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
 		DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
 		      "Attempt to write past end of page\n");
 		      "Attempt to write past end of page\n");
 		return -EINVAL;
 		return -EINVAL;
@@ -1745,7 +1777,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 	if (page == chip->pagebuf)
 	if (page == chip->pagebuf)
 		chip->pagebuf = -1;
 		chip->pagebuf = -1;
 
 
-	chip->oob_poi = chip->buffers->oobwbuf;
 	memset(chip->oob_poi, 0xff, mtd->oobsize);
 	memset(chip->oob_poi, 0xff, mtd->oobsize);
 	nand_fill_oob(chip, ops->oobbuf, ops);
 	nand_fill_oob(chip, ops->oobbuf, ops);
 	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
 	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
@@ -1754,7 +1785,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 	if (status)
 	if (status)
 		return status;
 		return status;
 
 
-	ops->retlen = ops->len;
+	ops->oobretlen = ops->ooblen;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1774,7 +1805,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
 	ops->retlen = 0;
 	ops->retlen = 0;
 
 
 	/* Do not allow writes past end of device */
 	/* Do not allow writes past end of device */
-	if ((to + ops->len) > mtd->size) {
+	if (ops->datbuf && (to + ops->len) > mtd->size) {
 		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
 		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
 		      "Attempt read beyond end of device\n");
 		      "Attempt read beyond end of device\n");
 		return -EINVAL;
 		return -EINVAL;
@@ -2188,8 +2219,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	/* Newer devices have all the information in additional id bytes */
 	/* Newer devices have all the information in additional id bytes */
 	if (!type->pagesize) {
 	if (!type->pagesize) {
 		int extid;
 		int extid;
-		/* The 3rd id byte contains non relevant data ATM */
-		extid = chip->read_byte(mtd);
+		/* The 3rd id byte holds MLC / multichip data */
+		chip->cellinfo = chip->read_byte(mtd);
 		/* The 4th id byte is the important one */
 		/* The 4th id byte is the important one */
 		extid = chip->read_byte(mtd);
 		extid = chip->read_byte(mtd);
 		/* Calc pagesize */
 		/* Calc pagesize */
@@ -2349,8 +2380,8 @@ int nand_scan_tail(struct mtd_info *mtd)
 	if (!chip->buffers)
 	if (!chip->buffers)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	/* Preset the internal oob write buffer */
-	memset(chip->buffers->oobwbuf, 0xff, mtd->oobsize);
+	/* Set the internal oob buffer location, just after the page data */
+	chip->oob_poi = chip->buffers->databuf + mtd->writesize;
 
 
 	/*
 	/*
 	 * If no default placement scheme is given, select an appropriate one
 	 * If no default placement scheme is given, select an appropriate one
@@ -2469,6 +2500,24 @@ int nand_scan_tail(struct mtd_info *mtd)
 	}
 	}
 	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
 	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
 
 
+	/*
+	 * Allow subpage writes up to ecc.steps. Not possible for MLC
+	 * FLASH.
+	 */
+	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
+	    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
+		switch(chip->ecc.steps) {
+		case 2:
+			mtd->subpage_sft = 1;
+			break;
+		case 4:
+		case 8:
+			mtd->subpage_sft = 2;
+			break;
+		}
+	}
+	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
 	/* Initialize state */
 	/* Initialize state */
 	chip->state = FL_READY;
 	chip->state = FL_READY;
 
 

+ 4 - 7
drivers/mtd/nand/nand_bbt.c

@@ -333,7 +333,6 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
 	struct mtd_oob_ops ops;
 	struct mtd_oob_ops ops;
 	int j, ret;
 	int j, ret;
 
 
-	ops.len = mtd->oobsize;
 	ops.ooblen = mtd->oobsize;
 	ops.ooblen = mtd->oobsize;
 	ops.oobbuf = buf;
 	ops.oobbuf = buf;
 	ops.ooboffs = 0;
 	ops.ooboffs = 0;
@@ -676,10 +675,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
 				       "bad block table\n");
 				       "bad block table\n");
 			}
 			}
 			/* Read oob data */
 			/* Read oob data */
-			ops.len = (len >> this->page_shift) * mtd->oobsize;
+			ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
 			ops.oobbuf = &buf[len];
 			ops.oobbuf = &buf[len];
 			res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
 			res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
-			if (res < 0 || ops.retlen != ops.len)
+			if (res < 0 || ops.oobretlen != ops.ooblen)
 				goto outerr;
 				goto outerr;
 
 
 			/* Calc the byte offset in the buffer */
 			/* Calc the byte offset in the buffer */
@@ -961,14 +960,12 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 	struct nand_bbt_descr *md = this->bbt_md;
 	struct nand_bbt_descr *md = this->bbt_md;
 
 
 	len = mtd->size >> (this->bbt_erase_shift + 2);
 	len = mtd->size >> (this->bbt_erase_shift + 2);
-	/* Allocate memory (2bit per block) */
-	this->bbt = kmalloc(len, GFP_KERNEL);
+	/* Allocate memory (2bit per block) and clear the memory bad block table */
+	this->bbt = kzalloc(len, GFP_KERNEL);
 	if (!this->bbt) {
 	if (!this->bbt) {
 		printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
 		printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
-	/* Clear the memory bad block table */
-	memset(this->bbt, 0x00, len);
 
 
 	/* If no primary table decriptor is given, scan the device
 	/* If no primary table decriptor is given, scan the device
 	 * to build a memory based bad block table
 	 * to build a memory based bad block table

+ 2 - 2
drivers/mtd/nand/nand_ecc.c

@@ -112,7 +112,7 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 	tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
 	tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
 
 
 	/* Calculate final ECC code */
 	/* Calculate final ECC code */
-#ifdef CONFIG_NAND_ECC_SMC
+#ifdef CONFIG_MTD_NAND_ECC_SMC
 	ecc_code[0] = ~tmp2;
 	ecc_code[0] = ~tmp2;
 	ecc_code[1] = ~tmp1;
 	ecc_code[1] = ~tmp1;
 #else
 #else
@@ -148,7 +148,7 @@ int nand_correct_data(struct mtd_info *mtd, u_char *dat,
 {
 {
 	uint8_t s0, s1, s2;
 	uint8_t s0, s1, s2;
 
 
-#ifdef CONFIG_NAND_ECC_SMC
+#ifdef CONFIG_MTD_NAND_ECC_SMC
 	s0 = calc_ecc[0] ^ read_ecc[0];
 	s0 = calc_ecc[0] ^ read_ecc[0];
 	s1 = calc_ecc[1] ^ read_ecc[1];
 	s1 = calc_ecc[1] ^ read_ecc[1];
 	s2 = calc_ecc[2] ^ read_ecc[2];
 	s2 = calc_ecc[2] ^ read_ecc[2];

+ 158 - 85
drivers/mtd/nand/nandsim.c

@@ -37,10 +37,6 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
-#ifdef CONFIG_NS_ABS_POS
-#include <asm/io.h>
-#endif
-
 
 
 /* Default simulator parameters values */
 /* Default simulator parameters values */
 #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE)  || \
 #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE)  || \
@@ -164,7 +160,7 @@ MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
 /* After a command is input, the simulator goes to one of the following states */
 /* After a command is input, the simulator goes to one of the following states */
 #define STATE_CMD_READ0        0x00000001 /* read data from the beginning of page */
 #define STATE_CMD_READ0        0x00000001 /* read data from the beginning of page */
 #define STATE_CMD_READ1        0x00000002 /* read data from the second half of page */
 #define STATE_CMD_READ1        0x00000002 /* read data from the second half of page */
-#define STATE_CMD_READSTART      0x00000003 /* read data second command (large page devices) */
+#define STATE_CMD_READSTART    0x00000003 /* read data second command (large page devices) */
 #define STATE_CMD_PAGEPROG     0x00000004 /* start page programm */
 #define STATE_CMD_PAGEPROG     0x00000004 /* start page programm */
 #define STATE_CMD_READOOB      0x00000005 /* read OOB area */
 #define STATE_CMD_READOOB      0x00000005 /* read OOB area */
 #define STATE_CMD_ERASE1       0x00000006 /* sector erase first command */
 #define STATE_CMD_ERASE1       0x00000006 /* sector erase first command */
@@ -230,6 +226,14 @@ MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
  */
  */
 #define NS_MAX_PREVSTATES 1
 #define NS_MAX_PREVSTATES 1
 
 
+/*
+ * A union to represent flash memory contents and flash buffer.
+ */
+union ns_mem {
+	u_char *byte;    /* for byte access */
+	uint16_t *word;  /* for 16-bit word access */
+};
+
 /*
 /*
  * The structure which describes all the internal simulator data.
  * The structure which describes all the internal simulator data.
  */
  */
@@ -247,17 +251,11 @@ struct nandsim {
 	uint16_t npstates;      /* number of previous states saved */
 	uint16_t npstates;      /* number of previous states saved */
 	uint16_t stateidx;      /* current state index */
 	uint16_t stateidx;      /* current state index */
 
 
-	/* The simulated NAND flash image */
-	union flash_media {
-		u_char *byte;
-		uint16_t    *word;
-	} mem;
+	/* The simulated NAND flash pages array */
+	union ns_mem *pages;
 
 
 	/* Internal buffer of page + OOB size bytes */
 	/* Internal buffer of page + OOB size bytes */
-	union internal_buffer {
-		u_char *byte;    /* for byte access */
-		uint16_t *word;  /* for 16-bit word access */
-	} buf;
+	union ns_mem buf;
 
 
 	/* NAND flash "geometry" */
 	/* NAND flash "geometry" */
 	struct nandsin_geometry {
 	struct nandsin_geometry {
@@ -345,13 +343,50 @@ static struct mtd_info *nsmtd;
 
 
 static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
 static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
 
 
+/*
+ * Allocate array of page pointers and initialize the array to NULL
+ * pointers.
+ *
+ * RETURNS: 0 if success, -ENOMEM if memory alloc fails.
+ */
+static int alloc_device(struct nandsim *ns)
+{
+	int i;
+
+	ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem));
+	if (!ns->pages) {
+		NS_ERR("alloc_map: unable to allocate page array\n");
+		return -ENOMEM;
+	}
+	for (i = 0; i < ns->geom.pgnum; i++) {
+		ns->pages[i].byte = NULL;
+	}
+
+	return 0;
+}
+
+/*
+ * Free any allocated pages, and free the array of page pointers.
+ */
+static void free_device(struct nandsim *ns)
+{
+	int i;
+
+	if (ns->pages) {
+		for (i = 0; i < ns->geom.pgnum; i++) {
+			if (ns->pages[i].byte)
+				kfree(ns->pages[i].byte);
+		}
+		vfree(ns->pages);
+	}
+}
+
 /*
 /*
  * Initialize the nandsim structure.
  * Initialize the nandsim structure.
  *
  *
  * RETURNS: 0 if success, -ERRNO if failure.
  * RETURNS: 0 if success, -ERRNO if failure.
  */
  */
-static int
-init_nandsim(struct mtd_info *mtd)
+static int init_nandsim(struct mtd_info *mtd)
 {
 {
 	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
 	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
 	struct nandsim   *ns   = (struct nandsim *)(chip->priv);
 	struct nandsim   *ns   = (struct nandsim *)(chip->priv);
@@ -405,7 +440,7 @@ init_nandsim(struct mtd_info *mtd)
 		}
 		}
 	} else {
 	} else {
 		if (ns->geom.totsz <= (128 << 20)) {
 		if (ns->geom.totsz <= (128 << 20)) {
-			ns->geom.pgaddrbytes  = 5;
+			ns->geom.pgaddrbytes  = 4;
 			ns->geom.secaddrbytes = 2;
 			ns->geom.secaddrbytes = 2;
 		} else {
 		} else {
 			ns->geom.pgaddrbytes  = 5;
 			ns->geom.pgaddrbytes  = 5;
@@ -439,23 +474,8 @@ init_nandsim(struct mtd_info *mtd)
 	printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);
 	printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);
 	printk("options: %#x\n",                ns->options);
 	printk("options: %#x\n",                ns->options);
 
 
-	/* Map / allocate and initialize the flash image */
-#ifdef CONFIG_NS_ABS_POS
-	ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob);
-	if (!ns->mem.byte) {
-		NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n",
-			(void *)CONFIG_NS_ABS_POS);
-		return -ENOMEM;
-	}
-#else
-	ns->mem.byte = vmalloc(ns->geom.totszoob);
-	if (!ns->mem.byte) {
-		NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n",
-			ns->geom.totszoob);
-		return -ENOMEM;
-	}
-	memset(ns->mem.byte, 0xFF, ns->geom.totszoob);
-#endif
+	if (alloc_device(ns) != 0)
+		goto error;
 
 
 	/* Allocate / initialize the internal buffer */
 	/* Allocate / initialize the internal buffer */
 	ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
 	ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
@@ -474,11 +494,7 @@ init_nandsim(struct mtd_info *mtd)
 	return 0;
 	return 0;
 
 
 error:
 error:
-#ifdef CONFIG_NS_ABS_POS
-	iounmap(ns->mem.byte);
-#else
-	vfree(ns->mem.byte);
-#endif
+	free_device(ns);
 
 
 	return -ENOMEM;
 	return -ENOMEM;
 }
 }
@@ -486,16 +502,10 @@ error:
 /*
 /*
  * Free the nandsim structure.
  * Free the nandsim structure.
  */
  */
-static void
-free_nandsim(struct nandsim *ns)
+static void free_nandsim(struct nandsim *ns)
 {
 {
 	kfree(ns->buf.byte);
 	kfree(ns->buf.byte);
-
-#ifdef CONFIG_NS_ABS_POS
-	iounmap(ns->mem.byte);
-#else
-	vfree(ns->mem.byte);
-#endif
+	free_device(ns);
 
 
 	return;
 	return;
 }
 }
@@ -503,8 +513,7 @@ free_nandsim(struct nandsim *ns)
 /*
 /*
  * Returns the string representation of 'state' state.
  * Returns the string representation of 'state' state.
  */
  */
-static char *
-get_state_name(uint32_t state)
+static char *get_state_name(uint32_t state)
 {
 {
 	switch (NS_STATE(state)) {
 	switch (NS_STATE(state)) {
 		case STATE_CMD_READ0:
 		case STATE_CMD_READ0:
@@ -562,8 +571,7 @@ get_state_name(uint32_t state)
  *
  *
  * RETURNS: 1 if wrong command, 0 if right.
  * RETURNS: 1 if wrong command, 0 if right.
  */
  */
-static int
-check_command(int cmd)
+static int check_command(int cmd)
 {
 {
 	switch (cmd) {
 	switch (cmd) {
 
 
@@ -589,8 +597,7 @@ check_command(int cmd)
 /*
 /*
  * Returns state after command is accepted by command number.
  * Returns state after command is accepted by command number.
  */
  */
-static uint32_t
-get_state_by_command(unsigned command)
+static uint32_t get_state_by_command(unsigned command)
 {
 {
 	switch (command) {
 	switch (command) {
 		case NAND_CMD_READ0:
 		case NAND_CMD_READ0:
@@ -626,8 +633,7 @@ get_state_by_command(unsigned command)
 /*
 /*
  * Move an address byte to the correspondent internal register.
  * Move an address byte to the correspondent internal register.
  */
  */
-static inline void
-accept_addr_byte(struct nandsim *ns, u_char bt)
+static inline void accept_addr_byte(struct nandsim *ns, u_char bt)
 {
 {
 	uint byte = (uint)bt;
 	uint byte = (uint)bt;
 
 
@@ -645,8 +651,7 @@ accept_addr_byte(struct nandsim *ns, u_char bt)
 /*
 /*
  * Switch to STATE_READY state.
  * Switch to STATE_READY state.
  */
  */
-static inline void
-switch_to_ready_state(struct nandsim *ns, u_char status)
+static inline void switch_to_ready_state(struct nandsim *ns, u_char status)
 {
 {
 	NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
 	NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
 
 
@@ -705,8 +710,7 @@ switch_to_ready_state(struct nandsim *ns, u_char status)
  *          -1 - several matches.
  *          -1 - several matches.
  *           0 - operation is found.
  *           0 - operation is found.
  */
  */
-static int
-find_operation(struct nandsim *ns, uint32_t flag)
+static int find_operation(struct nandsim *ns, uint32_t flag)
 {
 {
 	int opsfound = 0;
 	int opsfound = 0;
 	int i, j, idx = 0;
 	int i, j, idx = 0;
@@ -790,15 +794,94 @@ find_operation(struct nandsim *ns, uint32_t flag)
 	return -1;
 	return -1;
 }
 }
 
 
+/*
+ * Returns a pointer to the current page.
+ */
+static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
+{
+	return &(ns->pages[ns->regs.row]);
+}
+
+/*
+ * Retuns a pointer to the current byte, within the current page.
+ */
+static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
+{
+	return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
+}
+
+/*
+ * Fill the NAND buffer with data read from the specified page.
+ */
+static void read_page(struct nandsim *ns, int num)
+{
+	union ns_mem *mypage;
+
+	mypage = NS_GET_PAGE(ns);
+	if (mypage->byte == NULL) {
+		NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
+		memset(ns->buf.byte, 0xFF, num);
+	} else {
+		NS_DBG("read_page: page %d allocated, reading from %d\n",
+			ns->regs.row, ns->regs.column + ns->regs.off);
+		memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
+	}
+}
+
+/*
+ * Erase all pages in the specified sector.
+ */
+static void erase_sector(struct nandsim *ns)
+{
+	union ns_mem *mypage;
+	int i;
+
+	mypage = NS_GET_PAGE(ns);
+	for (i = 0; i < ns->geom.pgsec; i++) {
+		if (mypage->byte != NULL) {
+			NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
+			kfree(mypage->byte);
+			mypage->byte = NULL;
+		}
+		mypage++;
+	}
+}
+
+/*
+ * Program the specified page with the contents from the NAND buffer.
+ */
+static int prog_page(struct nandsim *ns, int num)
+{
+	int i;
+	union ns_mem *mypage;
+	u_char *pg_off;
+
+	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);
+		if (mypage->byte == NULL) {
+			NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
+			return -1;
+		}
+		memset(mypage->byte, 0xFF, ns->geom.pgszoob);
+	}
+
+	pg_off = NS_PAGE_BYTE_OFF(ns);
+	for (i = 0; i < num; i++)
+		pg_off[i] &= ns->buf.byte[i];
+
+	return 0;
+}
+
 /*
 /*
  * If state has any action bit, perform this action.
  * If state has any action bit, perform this action.
  *
  *
  * RETURNS: 0 if success, -1 if error.
  * RETURNS: 0 if success, -1 if error.
  */
  */
-static int
-do_state_action(struct nandsim *ns, uint32_t action)
+static int do_state_action(struct nandsim *ns, uint32_t action)
 {
 {
-	int i, num;
+	int num;
 	int busdiv = ns->busw == 8 ? 1 : 2;
 	int busdiv = ns->busw == 8 ? 1 : 2;
 
 
 	action &= ACTION_MASK;
 	action &= ACTION_MASK;
@@ -822,7 +905,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
 			break;
 			break;
 		}
 		}
 		num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
 		num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
-		memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num);
+		read_page(ns, num);
 
 
 		NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
 		NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
 			num, NS_RAW_OFFSET(ns) + ns->regs.off);
 			num, NS_RAW_OFFSET(ns) + ns->regs.off);
@@ -863,7 +946,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
 				ns->regs.row, NS_RAW_OFFSET(ns));
 				ns->regs.row, NS_RAW_OFFSET(ns));
 		NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
 		NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
 
 
-		memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob);
+		erase_sector(ns);
 
 
 		NS_MDELAY(erase_delay);
 		NS_MDELAY(erase_delay);
 
 
@@ -886,8 +969,8 @@ do_state_action(struct nandsim *ns, uint32_t action)
 			return -1;
 			return -1;
 		}
 		}
 
 
-		for (i = 0; i < num; i++)
-			ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i];
+		if (prog_page(ns, num) == -1)
+			return -1;
 
 
 		NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
 		NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
 			num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
 			num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
@@ -928,8 +1011,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
 /*
 /*
  * Switch simulator's state.
  * Switch simulator's state.
  */
  */
-static void
-switch_state(struct nandsim *ns)
+static void switch_state(struct nandsim *ns)
 {
 {
 	if (ns->op) {
 	if (ns->op) {
 		/*
 		/*
@@ -1070,8 +1152,7 @@ switch_state(struct nandsim *ns)
 	}
 	}
 }
 }
 
 
-static u_char
-ns_nand_read_byte(struct mtd_info *mtd)
+static u_char ns_nand_read_byte(struct mtd_info *mtd)
 {
 {
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
 	u_char outb = 0x00;
 	u_char outb = 0x00;
@@ -1144,8 +1225,7 @@ ns_nand_read_byte(struct mtd_info *mtd)
 	return outb;
 	return outb;
 }
 }
 
 
-static void
-ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
+static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
 {
 {
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
 
 
@@ -1308,15 +1388,13 @@ static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
 		ns_nand_write_byte(mtd, cmd);
 		ns_nand_write_byte(mtd, cmd);
 }
 }
 
 
-static int
-ns_device_ready(struct mtd_info *mtd)
+static int ns_device_ready(struct mtd_info *mtd)
 {
 {
 	NS_DBG("device_ready\n");
 	NS_DBG("device_ready\n");
 	return 1;
 	return 1;
 }
 }
 
 
-static uint16_t
-ns_nand_read_word(struct mtd_info *mtd)
+static uint16_t ns_nand_read_word(struct mtd_info *mtd)
 {
 {
 	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
 	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
 
 
@@ -1325,8 +1403,7 @@ ns_nand_read_word(struct mtd_info *mtd)
 	return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
 	return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
 }
 }
 
 
-static void
-ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
 
 
@@ -1353,8 +1430,7 @@ ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 	}
 	}
 }
 }
 
 
-static void
-ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 {
 {
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
         struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
 
 
@@ -1407,8 +1483,7 @@ ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 	return;
 	return;
 }
 }
 
 
-static int
-ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+static int ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
 	ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
 	ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
 
 
@@ -1436,14 +1511,12 @@ static int __init ns_init_module(void)
 	}
 	}
 
 
 	/* Allocate and initialize mtd_info, nand_chip and nandsim structures */
 	/* Allocate and initialize mtd_info, nand_chip and nandsim structures */
-	nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
+	nsmtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
 				+ sizeof(struct nandsim), GFP_KERNEL);
 				+ sizeof(struct nandsim), GFP_KERNEL);
 	if (!nsmtd) {
 	if (!nsmtd) {
 		NS_ERR("unable to allocate core structures.\n");
 		NS_ERR("unable to allocate core structures.\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
-	memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) +
-			sizeof(struct nandsim));
 	chip        = (struct nand_chip *)(nsmtd + 1);
 	chip        = (struct nand_chip *)(nsmtd + 1);
         nsmtd->priv = (void *)chip;
         nsmtd->priv = (void *)chip;
 	nand        = (struct nandsim *)(chip + 1);
 	nand        = (struct nandsim *)(chip + 1);

+ 1 - 1
drivers/mtd/nand/ndfc.c

@@ -56,7 +56,7 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip)
 		ccr |= NDFC_CCR_BS(chip + pchip->chip_offset);
 		ccr |= NDFC_CCR_BS(chip + pchip->chip_offset);
 	} else
 	} else
 		ccr |= NDFC_CCR_RESET_CE;
 		ccr |= NDFC_CCR_RESET_CE;
-	writel(ccr, ndfc->ndfcbase + NDFC_CCR);
+	__raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
 }
 }
 
 
 static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)

+ 3 - 43
drivers/mtd/nand/rtc_from4.c

@@ -24,6 +24,7 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/rslib.h>
 #include <linux/rslib.h>
+#include <linux/bitrev.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/mtd.h>
@@ -152,47 +153,6 @@ static struct nand_ecclayout rtc_from4_nand_oobinfo = {
 	.oobfree = {{32, 32}}
 	.oobfree = {{32, 32}}
 };
 };
 
 
-/* Aargh. I missed the reversed bit order, when I
- * was talking to Renesas about the FPGA.
- *
- * The table is used for bit reordering and inversion
- * of the ecc byte which we get from the FPGA
- */
-static uint8_t revbits[256] = {
-	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
-	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
-	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
-	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
-	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
-	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
-	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
-	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
-	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
-	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
-	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
-	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
-	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
-	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
-	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
-	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
-	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
-	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
-	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
-	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
-	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
-	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
-	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
-	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
-	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
-	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
-	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
-	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
-	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
-	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
-	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
-	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
-};
-
 #endif
 #endif
 
 
 /*
 /*
@@ -397,7 +357,7 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
 	/* Read the syndrom pattern from the FPGA and correct the bitorder */
 	/* Read the syndrom pattern from the FPGA and correct the bitorder */
 	rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC);
 	rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC);
 	for (i = 0; i < 8; i++) {
 	for (i = 0; i < 8; i++) {
-		ecc[i] = revbits[(*rs_ecc) & 0xFF];
+		ecc[i] = bitrev8(*rs_ecc);
 		rs_ecc++;
 		rs_ecc++;
 	}
 	}
 
 
@@ -496,7 +456,7 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this,
 		rtn = nand_do_read(mtd, page, len, &retlen, buf);
 		rtn = nand_do_read(mtd, page, len, &retlen, buf);
 
 
 		/* if read failed or > 1-bit error corrected */
 		/* if read failed or > 1-bit error corrected */
-		if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) {
+		if (rtn || (mtd->ecc_stats.corrected - corrected) > 1)
 			er_stat |= 1 << 1;
 			er_stat |= 1 << 1;
 		kfree(buf);
 		kfree(buf);
 	}
 	}

+ 1 - 1
drivers/mtd/nand/s3c2410.c

@@ -283,7 +283,7 @@ static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
 				   unsigned int ctrl)
 				   unsigned int ctrl)
 {
 {
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-	
+
 	if (cmd == NAND_CMD_NONE)
 	if (cmd == NAND_CMD_NONE)
 		return;
 		return;
 
 

+ 5 - 7
drivers/mtd/nftlcore.c

@@ -57,17 +57,16 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 
 
 	DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
 	DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
 
 
-	nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
+	nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
 
 
 	if (!nftl) {
 	if (!nftl) {
 		printk(KERN_WARNING "NFTL: out of memory for data structures\n");
 		printk(KERN_WARNING "NFTL: out of memory for data structures\n");
 		return;
 		return;
 	}
 	}
-	memset(nftl, 0, sizeof(*nftl));
 
 
 	nftl->mbd.mtd = mtd;
 	nftl->mbd.mtd = mtd;
 	nftl->mbd.devnum = -1;
 	nftl->mbd.devnum = -1;
-	nftl->mbd.blksize = 512;
+
 	nftl->mbd.tr = tr;
 	nftl->mbd.tr = tr;
 
 
         if (NFTL_mount(nftl) < 0) {
         if (NFTL_mount(nftl) < 0) {
@@ -147,10 +146,9 @@ int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 	ops.ooblen = len;
 	ops.ooblen = len;
 	ops.oobbuf = buf;
 	ops.oobbuf = buf;
 	ops.datbuf = NULL;
 	ops.datbuf = NULL;
-	ops.len = len;
 
 
 	res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
 	res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
-	*retlen = ops.retlen;
+	*retlen = ops.oobretlen;
 	return res;
 	return res;
 }
 }
 
 
@@ -168,10 +166,9 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
 	ops.ooblen = len;
 	ops.ooblen = len;
 	ops.oobbuf = buf;
 	ops.oobbuf = buf;
 	ops.datbuf = NULL;
 	ops.datbuf = NULL;
-	ops.len = len;
 
 
 	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
 	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
-	*retlen = ops.retlen;
+	*retlen = ops.oobretlen;
 	return res;
 	return res;
 }
 }
 
 
@@ -797,6 +794,7 @@ static struct mtd_blktrans_ops nftl_tr = {
 	.name		= "nftl",
 	.name		= "nftl",
 	.major		= NFTL_MAJOR,
 	.major		= NFTL_MAJOR,
 	.part_bits	= NFTL_PARTN_BITS,
 	.part_bits	= NFTL_PARTN_BITS,
+	.blksize 	= 512,
 	.getgeo		= nftl_getgeo,
 	.getgeo		= nftl_getgeo,
 	.readsect	= nftl_readblock,
 	.readsect	= nftl_readblock,
 #ifdef CONFIG_NFTL_RW
 #ifdef CONFIG_NFTL_RW

+ 2 - 3
drivers/mtd/onenand/generic.c

@@ -45,12 +45,10 @@ static int __devinit generic_onenand_probe(struct device *dev)
 	unsigned long size = res->end - res->start + 1;
 	unsigned long size = res->end - res->start + 1;
 	int err;
 	int err;
 
 
-	info = kmalloc(sizeof(struct onenand_info), GFP_KERNEL);
+	info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
 	if (!info)
 	if (!info)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(info, 0, sizeof(struct onenand_info));
-
 	if (!request_mem_region(res->start, size, dev->driver->name)) {
 	if (!request_mem_region(res->start, size, dev->driver->name)) {
 		err = -EBUSY;
 		err = -EBUSY;
 		goto out_free_info;
 		goto out_free_info;
@@ -63,6 +61,7 @@ static int __devinit generic_onenand_probe(struct device *dev)
 	}
 	}
 
 
 	info->onenand.mmcontrol = pdata->mmcontrol;
 	info->onenand.mmcontrol = pdata->mmcontrol;
+	info->onenand.irq = platform_get_irq(pdev, 0);
 
 
 	info->mtd.name = pdev->dev.bus_id;
 	info->mtd.name = pdev->dev.bus_id;
 	info->mtd.priv = &info->onenand;
 	info->mtd.priv = &info->onenand;

+ 283 - 91
drivers/mtd/onenand/onenand_base.c

@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
+#include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/jiffies.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/onenand.h>
@@ -191,8 +192,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
 	int value, readcmd = 0, block_cmd = 0;
 	int value, readcmd = 0, block_cmd = 0;
 	int block, page;
 	int block, page;
-	/* Now we use page size operation */
-	int sectors = 4, count = 4;
 
 
 	/* Address translation */
 	/* Address translation */
 	switch (cmd) {
 	switch (cmd) {
@@ -244,6 +243,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 	}
 	}
 
 
 	if (page != -1) {
 	if (page != -1) {
+		/* Now we use page size operation */
+		int sectors = 4, count = 4;
 		int dataram;
 		int dataram;
 
 
 		switch (cmd) {
 		switch (cmd) {
@@ -297,7 +298,7 @@ static int onenand_wait(struct mtd_info *mtd, int state)
 	unsigned long timeout;
 	unsigned long timeout;
 	unsigned int flags = ONENAND_INT_MASTER;
 	unsigned int flags = ONENAND_INT_MASTER;
 	unsigned int interrupt = 0;
 	unsigned int interrupt = 0;
-	unsigned int ctrl, ecc;
+	unsigned int ctrl;
 
 
 	/* The 20 msec is enough */
 	/* The 20 msec is enough */
 	timeout = jiffies + msecs_to_jiffies(20);
 	timeout = jiffies + msecs_to_jiffies(20);
@@ -309,7 +310,6 @@ static int onenand_wait(struct mtd_info *mtd, int state)
 
 
 		if (state != FL_READING)
 		if (state != FL_READING)
 			cond_resched();
 			cond_resched();
-		touch_softlockup_watchdog();
 	}
 	}
 	/* To get correct interrupt status in timeout case */
 	/* To get correct interrupt status in timeout case */
 	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
 	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
@@ -317,28 +317,126 @@ static int onenand_wait(struct mtd_info *mtd, int state)
 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
 
 	if (ctrl & ONENAND_CTRL_ERROR) {
 	if (ctrl & ONENAND_CTRL_ERROR) {
-		/* It maybe occur at initial bad block */
 		DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl);
 		DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl);
-		/* Clear other interrupt bits for preventing ECC error */
-		interrupt &= ONENAND_INT_MASTER;
-	}
-
-	if (ctrl & ONENAND_CTRL_LOCK) {
-		DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x\n", ctrl);
-		return -EACCES;
+		if (ctrl & ONENAND_CTRL_LOCK)
+			DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n");
+		return ctrl;
 	}
 	}
 
 
 	if (interrupt & ONENAND_INT_READ) {
 	if (interrupt & ONENAND_INT_READ) {
-		ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
-		if (ecc & ONENAND_ECC_2BIT_ALL) {
+		int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+		if (ecc) {
 			DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
 			DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
-			return -EBADMSG;
+			if (ecc & ONENAND_ECC_2BIT_ALL) {
+				mtd->ecc_stats.failed++;
+				return ecc;
+			} else if (ecc & ONENAND_ECC_1BIT_ALL)
+				mtd->ecc_stats.corrected++;
 		}
 		}
 	}
 	}
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ * onenand_interrupt - [DEFAULT] onenand interrupt handler
+ * @param irq		onenand interrupt number
+ * @param dev_id	interrupt data
+ *
+ * complete the work
+ */
+static irqreturn_t onenand_interrupt(int irq, void *data)
+{
+	struct onenand_chip *this = (struct onenand_chip *) data;
+
+	/* To handle shared interrupt */
+	if (!this->complete.done)
+		complete(&this->complete);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * onenand_interrupt_wait - [DEFAULT] wait until the command is done
+ * @param mtd		MTD device structure
+ * @param state		state to select the max. timeout value
+ *
+ * Wait for command done.
+ */
+static int onenand_interrupt_wait(struct mtd_info *mtd, int state)
+{
+	struct onenand_chip *this = mtd->priv;
+
+	wait_for_completion(&this->complete);
+
+	return onenand_wait(mtd, state);
+}
+
+/*
+ * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait
+ * @param mtd		MTD device structure
+ * @param state		state to select the max. timeout value
+ *
+ * Try interrupt based wait (It is used one-time)
+ */
+static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state)
+{
+	struct onenand_chip *this = mtd->priv;
+	unsigned long remain, timeout;
+
+	/* We use interrupt wait first */
+	this->wait = onenand_interrupt_wait;
+
+	timeout = msecs_to_jiffies(100);
+	remain = wait_for_completion_timeout(&this->complete, timeout);
+	if (!remain) {
+		printk(KERN_INFO "OneNAND: There's no interrupt. "
+				"We use the normal wait\n");
+
+		/* Release the irq */
+		free_irq(this->irq, this);
+
+		this->wait = onenand_wait;
+	}
+
+	return onenand_wait(mtd, state);
+}
+
+/*
+ * onenand_setup_wait - [OneNAND Interface] setup onenand wait method
+ * @param mtd		MTD device structure
+ *
+ * There's two method to wait onenand work
+ * 1. polling - read interrupt status register
+ * 2. interrupt - use the kernel interrupt method
+ */
+static void onenand_setup_wait(struct mtd_info *mtd)
+{
+	struct onenand_chip *this = mtd->priv;
+	int syscfg;
+
+	init_completion(&this->complete);
+
+	if (this->irq <= 0) {
+		this->wait = onenand_wait;
+		return;
+	}
+
+	if (request_irq(this->irq, &onenand_interrupt,
+				IRQF_SHARED, "onenand", this)) {
+		/* If we can't get irq, use the normal wait */
+		this->wait = onenand_wait;
+		return;
+	}
+
+	/* Enable interrupt */
+	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+	syscfg |= ONENAND_SYS_CFG1_IOBE;
+	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+
+	this->wait = onenand_try_interrupt_wait;
+}
+
 /**
 /**
  * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
  * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
  * @param mtd		MTD data structure
  * @param mtd		MTD data structure
@@ -609,9 +707,10 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
 	size_t *retlen, u_char *buf)
 	size_t *retlen, u_char *buf)
 {
 {
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
+	struct mtd_ecc_stats stats;
 	int read = 0, column;
 	int read = 0, column;
 	int thislen;
 	int thislen;
-	int ret = 0;
+	int ret = 0, boundary = 0;
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
 	DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
 
 
@@ -627,38 +726,61 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
 
 
 	/* TODO handling oob */
 	/* TODO handling oob */
 
 
-	while (read < len) {
-		thislen = min_t(int, mtd->writesize, len - read);
-
-		column = from & (mtd->writesize - 1);
-		if (column + thislen > mtd->writesize)
-			thislen = mtd->writesize - column;
-
-		if (!onenand_check_bufferram(mtd, from)) {
-			this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize);
-
-			ret = this->wait(mtd, FL_READING);
-			/* First copy data and check return value for ECC handling */
-			onenand_update_bufferram(mtd, from, 1);
-		}
-
-		this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
-
-		read += thislen;
-
-		if (read == len)
-			break;
-
-		if (ret) {
-			DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: read failed = %d\n", ret);
-			goto out;
-		}
-
-		from += thislen;
-		buf += thislen;
-	}
+	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 (this->device_id & ONENAND_DEVICE_IS_DDP &&
+ 					unlikely(from == (this->chipsize >> 1))) {
+ 				this->write_word(0, 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(0x8000, 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);
+ 	}
 
 
-out:
 	/* Deselect and wake up anyone waiting on the device */
 	/* Deselect and wake up anyone waiting on the device */
 	onenand_release_device(mtd);
 	onenand_release_device(mtd);
 
 
@@ -668,7 +790,14 @@ out:
 	 * retlen == desired len and result == -EBADMSG
 	 * retlen == desired len and result == -EBADMSG
 	 */
 	 */
 	*retlen = read;
 	*retlen = read;
-	return ret;
+
+	if (mtd->ecc_stats.failed - stats.failed)
+		return -EBADMSG;
+
+	if (ret)
+		return ret;
+
+	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
 }
 }
 
 
 /**
 /**
@@ -705,6 +834,8 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
 	column = from & (mtd->oobsize - 1);
 	column = from & (mtd->oobsize - 1);
 
 
 	while (read < len) {
 	while (read < len) {
+		cond_resched();
+
 		thislen = mtd->oobsize - column;
 		thislen = mtd->oobsize - column;
 		thislen = min_t(int, thislen, len);
 		thislen = min_t(int, thislen, len);
 
 
@@ -717,16 +848,16 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
 
 
 		this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
 		this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
 
 
+		if (ret) {
+			DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
+			goto out;
+		}
+
 		read += thislen;
 		read += thislen;
 
 
 		if (read == len)
 		if (read == len)
 			break;
 			break;
 
 
-		if (ret) {
-			DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = %d\n", ret);
-			goto out;
-		}
-
 		buf += thislen;
 		buf += thislen;
 
 
 		/* Read more? */
 		/* Read more? */
@@ -756,8 +887,8 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
 {
 {
 	BUG_ON(ops->mode != MTD_OOB_PLACE);
 	BUG_ON(ops->mode != MTD_OOB_PLACE);
 
 
-	return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->len,
-				   &ops->retlen, ops->oobbuf);
+	return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
+				   &ops->oobretlen, ops->oobbuf);
 }
 }
 
 
 #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
 #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -804,6 +935,10 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr)
 	void __iomem *dataram0, *dataram1;
 	void __iomem *dataram0, *dataram1;
 	int ret = 0;
 	int ret = 0;
 
 
+	/* In partial page write, just skip it */
+	if ((addr & (mtd->writesize - 1)) != 0)
+		return 0;
+
 	this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
 	this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
 
 
 	ret = this->wait(mtd, FL_READING);
 	ret = this->wait(mtd, FL_READING);
@@ -826,7 +961,7 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr)
 #define onenand_verify_oob(...)		(0)
 #define onenand_verify_oob(...)		(0)
 #endif
 #endif
 
 
-#define NOTALIGNED(x)	((x & (mtd->writesize - 1)) != 0)
+#define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)
 
 
 /**
 /**
  * onenand_write - [MTD Interface] write buffer to FLASH
  * onenand_write - [MTD Interface] write buffer to FLASH
@@ -844,6 +979,7 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
 	int written = 0;
 	int written = 0;
 	int ret = 0;
 	int ret = 0;
+	int column, subpage;
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
 	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
 
 
@@ -862,45 +998,63 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
                 return -EINVAL;
                 return -EINVAL;
         }
         }
 
 
+	column = to & (mtd->writesize - 1);
+	subpage = column || (len & (mtd->writesize - 1));
+
 	/* Grab the lock and see if the device is available */
 	/* Grab the lock and see if the device is available */
 	onenand_get_device(mtd, FL_WRITING);
 	onenand_get_device(mtd, FL_WRITING);
 
 
 	/* Loop until all data write */
 	/* Loop until all data write */
 	while (written < len) {
 	while (written < len) {
-		int thislen = min_t(int, mtd->writesize, len - written);
-
-		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->writesize);
+		int bytes = mtd->writesize;
+		int thislen = min_t(int, bytes, len - written);
+		u_char *wbuf = (u_char *) buf;
+
+		cond_resched();
+
+		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes);
+
+		/* Partial page write */
+		if (subpage) {
+			bytes = min_t(int, bytes - column, (int) len);
+			memset(this->page_buf, 0xff, mtd->writesize);
+			memcpy(this->page_buf + column, buf, bytes);
+			wbuf = this->page_buf;
+			/* Even though partial write, we need page size */
+			thislen = mtd->writesize;
+		}
 
 
-		this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen);
+		this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen);
 		this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
 		this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
 
 
 		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
 		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
 
 
-		onenand_update_bufferram(mtd, to, 1);
+		/* In partial page write we don't update bufferram */
+		onenand_update_bufferram(mtd, to, !subpage);
 
 
 		ret = this->wait(mtd, FL_WRITING);
 		ret = this->wait(mtd, FL_WRITING);
 		if (ret) {
 		if (ret) {
 			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret);
 			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret);
-			goto out;
+			break;
 		}
 		}
 
 
-		written += thislen;
-
 		/* Only check verify write turn on */
 		/* Only check verify write turn on */
-		ret = onenand_verify_page(mtd, (u_char *) buf, to);
+		ret = onenand_verify_page(mtd, (u_char *) wbuf, to);
 		if (ret) {
 		if (ret) {
 			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret);
 			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret);
-			goto out;
+			break;
 		}
 		}
 
 
+		written += thislen;
+
 		if (written == len)
 		if (written == len)
 			break;
 			break;
 
 
+		column = 0;
 		to += thislen;
 		to += thislen;
 		buf += thislen;
 		buf += thislen;
 	}
 	}
 
 
-out:
 	/* Deselect and wake up anyone waiting on the device */
 	/* Deselect and wake up anyone waiting on the device */
 	onenand_release_device(mtd);
 	onenand_release_device(mtd);
 
 
@@ -944,6 +1098,8 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
 	while (written < len) {
 	while (written < len) {
 		int thislen = min_t(int, mtd->oobsize, len - written);
 		int thislen = min_t(int, mtd->oobsize, len - written);
 
 
+		cond_resched();
+
 		column = to & (mtd->oobsize - 1);
 		column = to & (mtd->oobsize - 1);
 
 
 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
@@ -999,8 +1155,8 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
 {
 {
 	BUG_ON(ops->mode != MTD_OOB_PLACE);
 	BUG_ON(ops->mode != MTD_OOB_PLACE);
 
 
-	return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->len,
-				    &ops->retlen, ops->oobbuf);
+	return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
+				    &ops->oobretlen, ops->oobbuf);
 }
 }
 
 
 /**
 /**
@@ -1071,6 +1227,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 	instr->state = MTD_ERASING;
 	instr->state = MTD_ERASING;
 
 
 	while (len) {
 	while (len) {
+		cond_resched();
 
 
 		/* Check if we have a bad block, we do not erase bad blocks */
 		/* Check if we have a bad block, we do not erase bad blocks */
 		if (onenand_block_checkbad(mtd, addr, 0, 0)) {
 		if (onenand_block_checkbad(mtd, addr, 0, 0)) {
@@ -1084,10 +1241,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 		ret = this->wait(mtd, FL_ERASING);
 		ret = this->wait(mtd, FL_ERASING);
 		/* Check, if it is write protected */
 		/* Check, if it is write protected */
 		if (ret) {
 		if (ret) {
-			if (ret == -EPERM)
-				DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Device is write protected!!!\n");
-			else
-				DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
+			DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
 			instr->state = MTD_ERASE_FAILED;
 			instr->state = MTD_ERASE_FAILED;
 			instr->fail_addr = addr;
 			instr->fail_addr = addr;
 			goto erase_exit;
 			goto erase_exit;
@@ -1129,7 +1283,6 @@ static void onenand_sync(struct mtd_info *mtd)
 	onenand_release_device(mtd);
 	onenand_release_device(mtd);
 }
 }
 
 
-
 /**
 /**
  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
  * @param mtd		MTD device structure
  * @param mtd		MTD device structure
@@ -1196,32 +1349,38 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 }
 }
 
 
 /**
 /**
- * onenand_unlock - [MTD Interface] Unlock block(s)
+ * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
  * @param mtd		MTD device structure
  * @param mtd		MTD device structure
  * @param ofs		offset relative to mtd start
  * @param ofs		offset relative to mtd start
- * @param len		number of bytes to unlock
+ * @param len		number of bytes to lock or unlock
  *
  *
- * Unlock one or more blocks
+ * Lock or unlock one or more blocks
  */
  */
-static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
 {
 {
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
 	int start, end, block, value, status;
 	int start, end, block, value, status;
+	int wp_status_mask;
 
 
 	start = ofs >> this->erase_shift;
 	start = ofs >> this->erase_shift;
 	end = len >> this->erase_shift;
 	end = len >> this->erase_shift;
 
 
+	if (cmd == ONENAND_CMD_LOCK)
+		wp_status_mask = ONENAND_WP_LS;
+	else
+		wp_status_mask = ONENAND_WP_US;
+
 	/* Continuous lock scheme */
 	/* Continuous lock scheme */
 	if (this->options & ONENAND_HAS_CONT_LOCK) {
 	if (this->options & ONENAND_HAS_CONT_LOCK) {
 		/* Set start block address */
 		/* Set start block address */
 		this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
 		this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
 		/* Set end block address */
 		/* Set end block address */
 		this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
 		this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
-		/* Write unlock command */
-		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
+		/* Write lock command */
+		this->command(mtd, cmd, 0, 0);
 
 
 		/* There's no return value */
 		/* There's no return value */
-		this->wait(mtd, FL_UNLOCKING);
+		this->wait(mtd, FL_LOCKING);
 
 
 		/* Sanity check */
 		/* Sanity check */
 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
@@ -1230,7 +1389,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 
 
 		/* Check lock status */
 		/* Check lock status */
 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
-		if (!(status & ONENAND_WP_US))
+		if (!(status & wp_status_mask))
 			printk(KERN_ERR "wp status = 0x%x\n", status);
 			printk(KERN_ERR "wp status = 0x%x\n", status);
 
 
 		return 0;
 		return 0;
@@ -1246,11 +1405,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 		/* Set start block address */
 		/* Set start block address */
 		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
 		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
-		/* Write unlock command */
-		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
+		/* Write lock command */
+		this->command(mtd, cmd, 0, 0);
 
 
 		/* There's no return value */
 		/* There's no return value */
-		this->wait(mtd, FL_UNLOCKING);
+		this->wait(mtd, FL_LOCKING);
 
 
 		/* Sanity check */
 		/* Sanity check */
 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
@@ -1259,13 +1418,39 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 
 
 		/* Check lock status */
 		/* Check lock status */
 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
-		if (!(status & ONENAND_WP_US))
+		if (!(status & wp_status_mask))
 			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
 			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
 	}
 	}
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ * onenand_lock - [MTD Interface] Lock block(s)
+ * @param mtd		MTD device structure
+ * @param ofs		offset relative to mtd start
+ * @param len		number of bytes to unlock
+ *
+ * Lock one or more blocks
+ */
+static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+	return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
+}
+
+/**
+ * onenand_unlock - [MTD Interface] Unlock block(s)
+ * @param mtd		MTD device structure
+ * @param ofs		offset relative to mtd start
+ * @param len		number of bytes to unlock
+ *
+ * Unlock one or more blocks
+ */
+static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+	return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
+}
+
 /**
 /**
  * onenand_check_lock_status - [OneNAND Interface] Check lock status
  * onenand_check_lock_status - [OneNAND Interface] Check lock status
  * @param this		onenand chip data structure
  * @param this		onenand chip data structure
@@ -1310,7 +1495,7 @@ static int onenand_unlock_all(struct mtd_info *mtd)
 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
 
 
 		/* There's no return value */
 		/* There's no return value */
-		this->wait(mtd, FL_UNLOCKING);
+		this->wait(mtd, FL_LOCKING);
 
 
 		/* Sanity check */
 		/* Sanity check */
 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
@@ -1334,7 +1519,7 @@ static int onenand_unlock_all(struct mtd_info *mtd)
 		return 0;
 		return 0;
 	}
 	}
 
 
-	mtd->unlock(mtd, 0x0, this->chipsize);
+	onenand_unlock(mtd, 0x0, this->chipsize);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1762,7 +1947,7 @@ static int onenand_probe(struct mtd_info *mtd)
 	/* Read manufacturer and device IDs from Register */
 	/* Read manufacturer and device IDs from Register */
 	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
 	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
-	ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID);
+	ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
 
 
 	/* Check OneNAND device */
 	/* Check OneNAND device */
 	if (maf_id != bram_maf_id || dev_id != bram_dev_id)
 	if (maf_id != bram_maf_id || dev_id != bram_dev_id)
@@ -1846,7 +2031,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 	if (!this->command)
 	if (!this->command)
 		this->command = onenand_command;
 		this->command = onenand_command;
 	if (!this->wait)
 	if (!this->wait)
-		this->wait = onenand_wait;
+		onenand_setup_wait(mtd);
 
 
 	if (!this->read_bufferram)
 	if (!this->read_bufferram)
 		this->read_bufferram = onenand_read_bufferram;
 		this->read_bufferram = onenand_read_bufferram;
@@ -1883,23 +2068,30 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 	init_waitqueue_head(&this->wq);
 	init_waitqueue_head(&this->wq);
 	spin_lock_init(&this->chip_lock);
 	spin_lock_init(&this->chip_lock);
 
 
+	/*
+	 * Allow subpage writes up to oobsize.
+	 */
 	switch (mtd->oobsize) {
 	switch (mtd->oobsize) {
 	case 64:
 	case 64:
 		this->ecclayout = &onenand_oob_64;
 		this->ecclayout = &onenand_oob_64;
+		mtd->subpage_sft = 2;
 		break;
 		break;
 
 
 	case 32:
 	case 32:
 		this->ecclayout = &onenand_oob_32;
 		this->ecclayout = &onenand_oob_32;
+		mtd->subpage_sft = 1;
 		break;
 		break;
 
 
 	default:
 	default:
 		printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
 		printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
 			mtd->oobsize);
 			mtd->oobsize);
+		mtd->subpage_sft = 0;
 		/* To prevent kernel oops */
 		/* To prevent kernel oops */
 		this->ecclayout = &onenand_oob_32;
 		this->ecclayout = &onenand_oob_32;
 		break;
 		break;
 	}
 	}
 
 
+	this->subpagesize = mtd->writesize >> mtd->subpage_sft;
 	mtd->ecclayout = this->ecclayout;
 	mtd->ecclayout = this->ecclayout;
 
 
 	/* Fill in remaining MTD driver data */
 	/* Fill in remaining MTD driver data */
@@ -1922,7 +2114,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 	mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
 	mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
 #endif
 #endif
 	mtd->sync = onenand_sync;
 	mtd->sync = onenand_sync;
-	mtd->lock = NULL;
+	mtd->lock = onenand_lock;
 	mtd->unlock = onenand_unlock;
 	mtd->unlock = onenand_unlock;
 	mtd->suspend = onenand_suspend;
 	mtd->suspend = onenand_suspend;
 	mtd->resume = onenand_resume;
 	mtd->resume = onenand_resume;

+ 6 - 8
drivers/mtd/onenand/onenand_bbt.c

@@ -93,13 +93,15 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 			ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
 			ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
 						  readlen, &retlen, &buf[0]);
 						  readlen, &retlen, &buf[0]);
 
 
-			if (ret)
+			/* If it is a initial bad block, just ignore it */
+			if (ret && !(ret & ONENAND_CTRL_LOAD))
 				return ret;
 				return ret;
 
 
 			if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
 			if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
 				bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
 				bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
 				printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
 				printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
 					i >> 1, (unsigned int) from);
 					i >> 1, (unsigned int) from);
+				mtd->ecc_stats.badblocks++;
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -177,14 +179,12 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 	int len, ret = 0;
 	int len, ret = 0;
 
 
 	len = mtd->size >> (this->erase_shift + 2);
 	len = mtd->size >> (this->erase_shift + 2);
-	/* Allocate memory (2bit per block) */
-	bbm->bbt = kmalloc(len, GFP_KERNEL);
+	/* Allocate memory (2bit per block) and clear the memory bad block table */
+	bbm->bbt = kzalloc(len, GFP_KERNEL);
 	if (!bbm->bbt) {
 	if (!bbm->bbt) {
 		printk(KERN_ERR "onenand_scan_bbt: Out of memory\n");
 		printk(KERN_ERR "onenand_scan_bbt: Out of memory\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
-	/* Clear the memory bad block table */
-	memset(bbm->bbt, 0x00, len);
 
 
 	/* Set the bad block position */
 	/* Set the bad block position */
 	bbm->badblockpos = ONENAND_BADBLOCK_POS;
 	bbm->badblockpos = ONENAND_BADBLOCK_POS;
@@ -230,14 +230,12 @@ int onenand_default_bbt(struct mtd_info *mtd)
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
 	struct bbm_info *bbm;
 	struct bbm_info *bbm;
 
 
-	this->bbm = kmalloc(sizeof(struct bbm_info), GFP_KERNEL);
+	this->bbm = kzalloc(sizeof(struct bbm_info), GFP_KERNEL);
 	if (!this->bbm)
 	if (!this->bbm)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	bbm = this->bbm;
 	bbm = this->bbm;
 
 
-	memset(bbm, 0, sizeof(struct bbm_info));
-
 	/* 1KB page has same configuration as 2KB page */
 	/* 1KB page has same configuration as 2KB page */
 	if (!bbm->badblock_pattern)
 	if (!bbm->badblock_pattern)
 		bbm->badblock_pattern = &largepage_memorybased;
 		bbm->badblock_pattern = &largepage_memorybased;

+ 24 - 6
drivers/mtd/redboot.c

@@ -96,7 +96,19 @@ static int parse_redboot_partitions(struct mtd_info *master,
 			 */
 			 */
 			if (swab32(buf[i].size) == master->erasesize) {
 			if (swab32(buf[i].size) == master->erasesize) {
 				int j;
 				int j;
-				for (j = 0; j < numslots && buf[j].name[0] != 0xff; ++j) {
+				for (j = 0; j < numslots; ++j) {
+
+					/* A single 0xff denotes a deleted entry.
+					 * Two of them in a row is the end of the table.
+					 */
+					if (buf[j].name[0] == 0xff) {
+				  		if (buf[j].name[1] == 0xff) {
+							break;
+						} else {
+							continue;
+						}
+					}
+
 					/* The unsigned long fields were written with the
 					/* The unsigned long fields were written with the
 					 * wrong byte sex, name and pad have no byte sex.
 					 * wrong byte sex, name and pad have no byte sex.
 					 */
 					 */
@@ -110,6 +122,9 @@ static int parse_redboot_partitions(struct mtd_info *master,
 				}
 				}
 			}
 			}
 			break;
 			break;
+		} else {
+			/* re-calculate of real numslots */
+			numslots = buf[i].size / sizeof(struct fis_image_desc);
 		}
 		}
 	}
 	}
 	if (i == numslots) {
 	if (i == numslots) {
@@ -123,8 +138,13 @@ static int parse_redboot_partitions(struct mtd_info *master,
 	for (i = 0; i < numslots; i++) {
 	for (i = 0; i < numslots; i++) {
 		struct fis_list *new_fl, **prev;
 		struct fis_list *new_fl, **prev;
 
 
-		if (buf[i].name[0] == 0xff)
-			continue;
+		if (buf[i].name[0] == 0xff) {
+			if (buf[i].name[1] == 0xff) {
+				break;
+			} else {
+				continue;
+			}
+		}
 		if (!redboot_checksum(&buf[i]))
 		if (!redboot_checksum(&buf[i]))
 			break;
 			break;
 
 
@@ -165,15 +185,13 @@ static int parse_redboot_partitions(struct mtd_info *master,
 		}
 		}
 	}
 	}
 #endif
 #endif
-	parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
+	parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
 
 
 	if (!parts) {
 	if (!parts) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto out;
 		goto out;
 	}
 	}
 
 
-	memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen);
-
 	nullname = (char *)&parts[nrparts];
 	nullname = (char *)&parts[nrparts];
 #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
 #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
 	if (nulllen > 0) {
 	if (nulllen > 0) {

+ 2 - 1
drivers/mtd/rfd_ftl.c

@@ -787,7 +787,6 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 
 
 	if (scan_header(part) == 0) {
 	if (scan_header(part) == 0) {
 		part->mbd.size = part->sector_count;
 		part->mbd.size = part->sector_count;
-		part->mbd.blksize = SECTOR_SIZE;
 		part->mbd.tr = tr;
 		part->mbd.tr = tr;
 		part->mbd.devnum = -1;
 		part->mbd.devnum = -1;
 		if (!(mtd->flags & MTD_WRITEABLE))
 		if (!(mtd->flags & MTD_WRITEABLE))
@@ -829,6 +828,8 @@ struct mtd_blktrans_ops rfd_ftl_tr = {
 	.name		= "rfd",
 	.name		= "rfd",
 	.major		= RFD_FTL_MAJOR,
 	.major		= RFD_FTL_MAJOR,
 	.part_bits	= PART_BITS,
 	.part_bits	= PART_BITS,
+	.blksize 	= SECTOR_SIZE,
+
 	.readsect	= rfd_ftl_readsect,
 	.readsect	= rfd_ftl_readsect,
 	.writesect	= rfd_ftl_writesect,
 	.writesect	= rfd_ftl_writesect,
 	.getgeo		= rfd_ftl_getgeo,
 	.getgeo		= rfd_ftl_getgeo,

+ 3 - 4
drivers/mtd/ssfdc.c

@@ -172,13 +172,12 @@ static int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf)
 
 
 	ops.mode = MTD_OOB_RAW;
 	ops.mode = MTD_OOB_RAW;
 	ops.ooboffs = 0;
 	ops.ooboffs = 0;
-	ops.ooblen = mtd->oobsize;
-	ops.len = OOB_SIZE;
+	ops.ooblen = OOB_SIZE;
 	ops.oobbuf = buf;
 	ops.oobbuf = buf;
 	ops.datbuf = NULL;
 	ops.datbuf = NULL;
 
 
 	ret = mtd->read_oob(mtd, offs, &ops);
 	ret = mtd->read_oob(mtd, offs, &ops);
-	if (ret < 0 || ops.retlen != OOB_SIZE)
+	if (ret < 0 || ops.oobretlen != OOB_SIZE)
 		return -1;
 		return -1;
 
 
 	return 0;
 	return 0;
@@ -312,7 +311,6 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 
 
 	ssfdc->mbd.mtd = mtd;
 	ssfdc->mbd.mtd = mtd;
 	ssfdc->mbd.devnum = -1;
 	ssfdc->mbd.devnum = -1;
-	ssfdc->mbd.blksize = SECTOR_SIZE;
 	ssfdc->mbd.tr = tr;
 	ssfdc->mbd.tr = tr;
 	ssfdc->mbd.readonly = 1;
 	ssfdc->mbd.readonly = 1;
 
 
@@ -447,6 +445,7 @@ static struct mtd_blktrans_ops ssfdcr_tr = {
 	.name		= "ssfdc",
 	.name		= "ssfdc",
 	.major		= SSFDCR_MAJOR,
 	.major		= SSFDCR_MAJOR,
 	.part_bits	= SSFDCR_PARTN_BITS,
 	.part_bits	= SSFDCR_PARTN_BITS,
+	.blksize	= SECTOR_SIZE,
 	.getgeo		= ssfdcr_getgeo,
 	.getgeo		= ssfdcr_getgeo,
 	.readsect	= ssfdcr_readsect,
 	.readsect	= ssfdcr_readsect,
 	.add_mtd	= ssfdcr_add_mtd,
 	.add_mtd	= ssfdcr_add_mtd,

+ 2 - 1
fs/jffs/jffs_fm.c

@@ -17,6 +17,7 @@
  *
  *
  */
  */
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/err.h>
 #include <linux/blkdev.h>
 #include <linux/blkdev.h>
 #include <linux/jffs.h>
 #include <linux/jffs.h>
 #include "jffs_fm.h"
 #include "jffs_fm.h"
@@ -104,7 +105,7 @@ jffs_build_begin(struct jffs_control *c, int unit)
 
 
 	mtd = get_mtd_device(NULL, unit);
 	mtd = get_mtd_device(NULL, unit);
 
 
-	if (!mtd) {
+	if (IS_ERR(mtd)) {
 		kfree(fmc);
 		kfree(fmc);
 		DJM(no_jffs_fmcontrol--);
 		DJM(no_jffs_fmcontrol--);
 		return NULL;
 		return NULL;

+ 2 - 2
fs/jffs2/debug.c

@@ -178,8 +178,8 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
 	while (ref2) {
 	while (ref2) {
 		uint32_t totlen = ref_totlen(c, jeb, ref2);
 		uint32_t totlen = ref_totlen(c, jeb, ref2);
 
 
-		if (ref2->flash_offset < jeb->offset ||
-				ref2->flash_offset > jeb->offset + c->sector_size) {
+		if (ref_offset(ref2) < jeb->offset ||
+				ref_offset(ref2) > jeb->offset + c->sector_size) {
 			JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n",
 			JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n",
 				ref_offset(ref2), jeb->offset);
 				ref_offset(ref2), jeb->offset);
 			goto error;
 			goto error;

+ 1 - 0
fs/jffs2/debug.h

@@ -13,6 +13,7 @@
 #ifndef _JFFS2_DEBUG_H_
 #ifndef _JFFS2_DEBUG_H_
 #define _JFFS2_DEBUG_H_
 #define _JFFS2_DEBUG_H_
 
 
+#include <linux/sched.h>
 
 
 #ifndef CONFIG_JFFS2_FS_DEBUG
 #ifndef CONFIG_JFFS2_FS_DEBUG
 #define CONFIG_JFFS2_FS_DEBUG 0
 #define CONFIG_JFFS2_FS_DEBUG 0

+ 1 - 2
fs/jffs2/fs.c

@@ -502,12 +502,11 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
+	c->inocache_list = kcalloc(INOCACHE_HASHSIZE, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
 	if (!c->inocache_list) {
 	if (!c->inocache_list) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto out_wbuf;
 		goto out_wbuf;
 	}
 	}
-	memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
 
 
 	jffs2_init_xattr_subsystem(c);
 	jffs2_init_xattr_subsystem(c);
 
 

+ 2 - 0
fs/jffs2/gc.c

@@ -838,6 +838,8 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
 
 
 		for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
 		for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
 
 
+			cond_resched();
+
 			/* We only care about obsolete ones */
 			/* We only care about obsolete ones */
 			if (!(ref_obsolete(raw)))
 			if (!(ref_obsolete(raw)))
 				continue;
 				continue;

+ 4 - 6
fs/jffs2/nodelist.h

@@ -294,23 +294,21 @@ static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev)
 
 
 static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
 static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
 {
 {
-	struct rb_node *node = root->rb_node;
+	struct rb_node *node = rb_first(root);
 
 
 	if (!node)
 	if (!node)
 		return NULL;
 		return NULL;
-	while(node->rb_left)
-		node = node->rb_left;
+
 	return rb_entry(node, struct jffs2_node_frag, rb);
 	return rb_entry(node, struct jffs2_node_frag, rb);
 }
 }
 
 
 static inline struct jffs2_node_frag *frag_last(struct rb_root *root)
 static inline struct jffs2_node_frag *frag_last(struct rb_root *root)
 {
 {
-	struct rb_node *node = root->rb_node;
+	struct rb_node *node = rb_last(root);
 
 
 	if (!node)
 	if (!node)
 		return NULL;
 		return NULL;
-	while(node->rb_right)
-		node = node->rb_right;
+
 	return rb_entry(node, struct jffs2_node_frag, rb);
 	return rb_entry(node, struct jffs2_node_frag, rb);
 }
 }
 
 

+ 1 - 2
fs/jffs2/readinode.c

@@ -944,13 +944,12 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
 int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
 {
 {
 	struct jffs2_raw_inode n;
 	struct jffs2_raw_inode n;
-	struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
+	struct jffs2_inode_info *f = kzalloc(sizeof(*f), GFP_KERNEL);
 	int ret;
 	int ret;
 
 
 	if (!f)
 	if (!f)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(f, 0, sizeof(*f));
 	init_MUTEX_LOCKED(&f->sem);
 	init_MUTEX_LOCKED(&f->sem);
 	f->inocache = ic;
 	f->inocache = ic;
 
 

+ 4 - 2
fs/jffs2/scan.c

@@ -128,17 +128,19 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
 	}
 	}
 
 
 	if (jffs2_sum_active()) {
 	if (jffs2_sum_active()) {
-		s = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
+		s = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
 		if (!s) {
 		if (!s) {
+			kfree(flashbuf);
 			JFFS2_WARNING("Can't allocate memory for summary\n");
 			JFFS2_WARNING("Can't allocate memory for summary\n");
 			return -ENOMEM;
 			return -ENOMEM;
 		}
 		}
-		memset(s, 0, sizeof(struct jffs2_summary));
 	}
 	}
 
 
 	for (i=0; i<c->nr_blocks; i++) {
 	for (i=0; i<c->nr_blocks; i++) {
 		struct jffs2_eraseblock *jeb = &c->blocks[i];
 		struct jffs2_eraseblock *jeb = &c->blocks[i];
 
 
+		cond_resched();
+
 		/* reset summary info for next eraseblock scan */
 		/* reset summary info for next eraseblock scan */
 		jffs2_sum_reset_collected(s);
 		jffs2_sum_reset_collected(s);
 
 

+ 3 - 3
fs/jffs2/summary.c

@@ -26,15 +26,13 @@
 
 
 int jffs2_sum_init(struct jffs2_sb_info *c)
 int jffs2_sum_init(struct jffs2_sb_info *c)
 {
 {
-	c->summary = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
+	c->summary = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
 
 
 	if (!c->summary) {
 	if (!c->summary) {
 		JFFS2_WARNING("Can't allocate memory for summary information!\n");
 		JFFS2_WARNING("Can't allocate memory for summary information!\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	memset(c->summary, 0, sizeof(struct jffs2_summary));
-
 	c->summary->sum_buf = vmalloc(c->sector_size);
 	c->summary->sum_buf = vmalloc(c->sector_size);
 
 
 	if (!c->summary->sum_buf) {
 	if (!c->summary->sum_buf) {
@@ -398,6 +396,8 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
 	for (i=0; i<je32_to_cpu(summary->sum_num); i++) {
 	for (i=0; i<je32_to_cpu(summary->sum_num); i++) {
 		dbg_summary("processing summary index %d\n", i);
 		dbg_summary("processing summary index %d\n", i);
 
 
+		cond_resched();
+
 		/* Make sure there's a spare ref for dirty space */
 		/* Make sure there's a spare ref for dirty space */
 		err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
 		err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
 		if (err)
 		if (err)

+ 4 - 3
fs/jffs2/super.c

@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/list.h>
 #include <linux/fs.h>
 #include <linux/fs.h>
+#include <linux/err.h>
 #include <linux/mount.h>
 #include <linux/mount.h>
 #include <linux/jffs2.h>
 #include <linux/jffs2.h>
 #include <linux/pagemap.h>
 #include <linux/pagemap.h>
@@ -184,9 +185,9 @@ static int jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
 
 
 	mtd = get_mtd_device(NULL, mtdnr);
 	mtd = get_mtd_device(NULL, mtdnr);
-	if (!mtd) {
+	if (IS_ERR(mtd)) {
 		D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
 		D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
-		return -EINVAL;
+		return PTR_ERR(mtd);
 	}
 	}
 
 
 	return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
 	return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
@@ -221,7 +222,7 @@ static int jffs2_get_sb(struct file_system_type *fs_type,
 			D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
 			D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
 			for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
 			for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
 				mtd = get_mtd_device(NULL, mtdnr);
 				mtd = get_mtd_device(NULL, mtdnr);
-				if (mtd) {
+				if (!IS_ERR(mtd)) {
 					if (!strcmp(mtd->name, dev_name+4))
 					if (!strcmp(mtd->name, dev_name+4))
 						return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
 						return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
 					put_mtd_device(mtd);
 					put_mtd_device(mtd);

+ 1 - 1
fs/jffs2/symlink.c

@@ -51,7 +51,7 @@ static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
 	 */
 	 */
 
 
 	if (!p) {
 	if (!p) {
-		printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
+		printk(KERN_ERR "jffs2_follow_link(): can't find symlink target\n");
 		p = ERR_PTR(-EIO);
 		p = ERR_PTR(-EIO);
 	}
 	}
 	D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target));
 	D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target));

+ 9 - 12
fs/jffs2/wbuf.c

@@ -969,8 +969,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
 	int oobsize = c->mtd->oobsize;
 	int oobsize = c->mtd->oobsize;
 	struct mtd_oob_ops ops;
 	struct mtd_oob_ops ops;
 
 
-	ops.len = NR_OOB_SCAN_PAGES * oobsize;
-	ops.ooblen = oobsize;
+	ops.ooblen = NR_OOB_SCAN_PAGES * oobsize;
 	ops.oobbuf = c->oobbuf;
 	ops.oobbuf = c->oobbuf;
 	ops.ooboffs = 0;
 	ops.ooboffs = 0;
 	ops.datbuf = NULL;
 	ops.datbuf = NULL;
@@ -983,10 +982,10 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
 		return ret;
 		return ret;
 	}
 	}
 
 
-	if (ops.retlen < ops.len) {
+	if (ops.oobretlen < ops.ooblen) {
 		D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
 		D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
 			  "returned short read (%zd bytes not %d) for block "
 			  "returned short read (%zd bytes not %d) for block "
-			  "at %08x\n", ops.retlen, ops.len, jeb->offset));
+			  "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset));
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
@@ -1005,7 +1004,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
 	}
 	}
 
 
 	/* we know, we are aligned :) */
 	/* we know, we are aligned :) */
-	for (page = oobsize; page < ops.len; page += sizeof(long)) {
+	for (page = oobsize; page < ops.ooblen; page += sizeof(long)) {
 		long dat = *(long *)(&ops.oobbuf[page]);
 		long dat = *(long *)(&ops.oobbuf[page]);
 		if(dat != -1)
 		if(dat != -1)
 			return 1;
 			return 1;
@@ -1033,7 +1032,6 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c,
 		return 2;
 		return 2;
 	}
 	}
 
 
-	ops.len = oobsize;
 	ops.ooblen = oobsize;
 	ops.ooblen = oobsize;
 	ops.oobbuf = c->oobbuf;
 	ops.oobbuf = c->oobbuf;
 	ops.ooboffs = 0;
 	ops.ooboffs = 0;
@@ -1048,10 +1046,10 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c,
 		return ret;
 		return ret;
 	}
 	}
 
 
-	if (ops.retlen < ops.len) {
+	if (ops.oobretlen < ops.ooblen) {
 		D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): "
 		D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): "
 			    "Read OOB return short read (%zd bytes not %d) "
 			    "Read OOB return short read (%zd bytes not %d) "
-			    "for block at %08x\n", ops.retlen, ops.len,
+			    "for block at %08x\n", ops.oobretlen, ops.ooblen,
 			    jeb->offset));
 			    jeb->offset));
 		return -EIO;
 		return -EIO;
 	}
 	}
@@ -1090,8 +1088,7 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
 	n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
 	n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
 	n.totlen = cpu_to_je32(8);
 	n.totlen = cpu_to_je32(8);
 
 
-	ops.len = c->fsdata_len;
-	ops.ooblen = c->fsdata_len;;
+	ops.ooblen = c->fsdata_len;
 	ops.oobbuf = (uint8_t *)&n;
 	ops.oobbuf = (uint8_t *)&n;
 	ops.ooboffs = c->fsdata_pos;
 	ops.ooboffs = c->fsdata_pos;
 	ops.datbuf = NULL;
 	ops.datbuf = NULL;
@@ -1105,10 +1102,10 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
 			  jeb->offset, ret));
 			  jeb->offset, ret));
 		return ret;
 		return ret;
 	}
 	}
-	if (ops.retlen != ops.len) {
+	if (ops.oobretlen != ops.ooblen) {
 		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
 		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
 			  "Short write for block at %08x: %zd not %d\n",
 			  "Short write for block at %08x: %zd not %d\n",
-			  jeb->offset, ops.retlen, ops.len));
+			  jeb->offset, ops.oobretlen, ops.ooblen));
 		return -EIO;
 		return -EIO;
 	}
 	}
 	return 0;
 	return 0;

+ 2 - 3
fs/jffs2/xattr.c

@@ -399,8 +399,6 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu
 {
 {
 	/* must be called under down_write(xattr_sem) */
 	/* must be called under down_write(xattr_sem) */
 	if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) {
 	if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) {
-		uint32_t xid = xd->xid, version = xd->version;
-
 		unload_xattr_datum(c, xd);
 		unload_xattr_datum(c, xd);
 		xd->flags |= JFFS2_XFLAGS_DEAD;
 		xd->flags |= JFFS2_XFLAGS_DEAD;
 		if (xd->node == (void *)xd) {
 		if (xd->node == (void *)xd) {
@@ -411,7 +409,8 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu
 		}
 		}
 		spin_unlock(&c->erase_completion_lock);
 		spin_unlock(&c->erase_completion_lock);
 
 
-		dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xid, version);
+		dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n",
+			  xd->xid, xd->version);
 	}
 	}
 }
 }
 
 

+ 2 - 1
include/linux/mtd/blktrans.h

@@ -24,7 +24,6 @@ struct mtd_blktrans_dev {
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
 	struct mutex lock;
 	struct mutex lock;
 	int devnum;
 	int devnum;
-	int blksize;
 	unsigned long size;
 	unsigned long size;
 	int readonly;
 	int readonly;
 	void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
 	void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
@@ -36,6 +35,8 @@ struct mtd_blktrans_ops {
 	char *name;
 	char *name;
 	int major;
 	int major;
 	int part_bits;
 	int part_bits;
+	int blksize;
+	int blkshift;
 
 
 	/* Access functions */
 	/* Access functions */
 	int (*readsect)(struct mtd_blktrans_dev *dev,
 	int (*readsect)(struct mtd_blktrans_dev *dev,

+ 16 - 8
include/linux/mtd/mtd.h

@@ -23,7 +23,7 @@
 
 
 #define MTD_CHAR_MAJOR 90
 #define MTD_CHAR_MAJOR 90
 #define MTD_BLOCK_MAJOR 31
 #define MTD_BLOCK_MAJOR 31
-#define MAX_MTD_DEVICES 16
+#define MAX_MTD_DEVICES 32
 
 
 #define MTD_ERASE_PENDING      	0x01
 #define MTD_ERASE_PENDING      	0x01
 #define MTD_ERASING		0x02
 #define MTD_ERASING		0x02
@@ -75,15 +75,12 @@ typedef enum {
  * struct mtd_oob_ops - oob operation operands
  * struct mtd_oob_ops - oob operation operands
  * @mode:	operation mode
  * @mode:	operation mode
  *
  *
- * @len:	number of bytes to write/read. When a data buffer is given
- *		(datbuf != NULL) this is the number of data bytes. When
- *		no data buffer is available this is the number of oob bytes.
+ * @len:	number of data bytes to write/read
  *
  *
- * @retlen:	number of bytes written/read. When a data buffer is given
- *		(datbuf != NULL) this is the number of data bytes. When
- *		no data buffer is available this is the number of oob bytes.
+ * @retlen:	number of data bytes written/read
  *
  *
- * @ooblen:	number of oob bytes per page
+ * @ooblen:	number of oob bytes to write/read
+ * @oobretlen:	number of oob bytes written/read
  * @ooboffs:	offset of oob data in the oob area (only relevant when
  * @ooboffs:	offset of oob data in the oob area (only relevant when
  *		mode = MTD_OOB_PLACE)
  *		mode = MTD_OOB_PLACE)
  * @datbuf:	data buffer - if NULL only oob data are read/written
  * @datbuf:	data buffer - if NULL only oob data are read/written
@@ -94,6 +91,7 @@ struct mtd_oob_ops {
 	size_t		len;
 	size_t		len;
 	size_t		retlen;
 	size_t		retlen;
 	size_t		ooblen;
 	size_t		ooblen;
+	size_t		oobretlen;
 	uint32_t	ooboffs;
 	uint32_t	ooboffs;
 	uint8_t		*datbuf;
 	uint8_t		*datbuf;
 	uint8_t		*oobbuf;
 	uint8_t		*oobbuf;
@@ -202,11 +200,20 @@ struct mtd_info {
 
 
 	/* ECC status information */
 	/* ECC status information */
 	struct mtd_ecc_stats ecc_stats;
 	struct mtd_ecc_stats ecc_stats;
+	/* Subpage shift (NAND) */
+	int subpage_sft;
 
 
 	void *priv;
 	void *priv;
 
 
 	struct module *owner;
 	struct module *owner;
 	int usecount;
 	int usecount;
+
+	/* If the driver is something smart, like UBI, it may need to maintain
+	 * its own reference counting. The below functions are only for driver.
+	 * The driver may register its callbacks. These callbacks are not
+	 * supposed to be called by MTD users */
+	int (*get_device) (struct mtd_info *mtd);
+	void (*put_device) (struct mtd_info *mtd);
 };
 };
 
 
 
 
@@ -216,6 +223,7 @@ extern int add_mtd_device(struct mtd_info *mtd);
 extern int del_mtd_device (struct mtd_info *mtd);
 extern int del_mtd_device (struct mtd_info *mtd);
 
 
 extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
 extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+extern struct mtd_info *get_mtd_device_nm(const char *name);
 
 
 extern void put_mtd_device(struct mtd_info *mtd);
 extern void put_mtd_device(struct mtd_info *mtd);
 
 

+ 10 - 5
include/linux/mtd/nand.h

@@ -166,6 +166,9 @@ typedef enum {
  * for all large page devices, as they do not support
  * for all large page devices, as they do not support
  * autoincrement.*/
  * autoincrement.*/
 #define NAND_NO_READRDY		0x00000100
 #define NAND_NO_READRDY		0x00000100
+/* Chip does not allow subpage writes */
+#define NAND_NO_SUBPAGE_WRITE	0x00000200
+
 
 
 /* Options valid for Samsung large page devices */
 /* Options valid for Samsung large page devices */
 #define NAND_SAMSUNG_LP_OPTIONS \
 #define NAND_SAMSUNG_LP_OPTIONS \
@@ -193,6 +196,9 @@ typedef enum {
 /* Nand scan has allocated controller struct */
 /* Nand scan has allocated controller struct */
 #define NAND_CONTROLLER_ALLOC	0x80000000
 #define NAND_CONTROLLER_ALLOC	0x80000000
 
 
+/* Cell info constants */
+#define NAND_CI_CHIPNR_MSK	0x03
+#define NAND_CI_CELLTYPE_MSK	0x0C
 
 
 /*
 /*
  * nand_state_t - chip states
  * nand_state_t - chip states
@@ -286,9 +292,7 @@ struct nand_ecc_ctrl {
  * struct nand_buffers - buffer structure for read/write
  * struct nand_buffers - buffer structure for read/write
  * @ecccalc:	buffer for calculated ecc
  * @ecccalc:	buffer for calculated ecc
  * @ecccode:	buffer for ecc read from flash
  * @ecccode:	buffer for ecc read from flash
- * @oobwbuf:	buffer for write oob data
  * @databuf:	buffer for data - dynamically sized
  * @databuf:	buffer for data - dynamically sized
- * @oobrbuf:	buffer to read oob data
  *
  *
  * Do not change the order of buffers. databuf and oobrbuf must be in
  * Do not change the order of buffers. databuf and oobrbuf must be in
  * consecutive order.
  * consecutive order.
@@ -296,9 +300,7 @@ struct nand_ecc_ctrl {
 struct nand_buffers {
 struct nand_buffers {
 	uint8_t	ecccalc[NAND_MAX_OOBSIZE];
 	uint8_t	ecccalc[NAND_MAX_OOBSIZE];
 	uint8_t	ecccode[NAND_MAX_OOBSIZE];
 	uint8_t	ecccode[NAND_MAX_OOBSIZE];
-	uint8_t	oobwbuf[NAND_MAX_OOBSIZE];
-	uint8_t databuf[NAND_MAX_PAGESIZE];
-	uint8_t	oobrbuf[NAND_MAX_OOBSIZE];
+	uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];
 };
 };
 
 
 /**
 /**
@@ -345,6 +347,7 @@ struct nand_buffers {
  * @chipsize:		[INTERN] the size of one chip for multichip arrays
  * @chipsize:		[INTERN] the size of one chip for multichip arrays
  * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
  * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
  * @pagebuf:		[INTERN] holds the pagenumber which is currently in data_buf
  * @pagebuf:		[INTERN] holds the pagenumber which is currently in data_buf
+ * @subpagesize:	[INTERN] holds the subpagesize
  * @ecclayout:		[REPLACEABLE] the default ecc placement scheme
  * @ecclayout:		[REPLACEABLE] the default ecc placement scheme
  * @bbt:		[INTERN] bad block table pointer
  * @bbt:		[INTERN] bad block table pointer
  * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash lookup
  * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash lookup
@@ -392,6 +395,8 @@ struct nand_chip {
 	unsigned long	chipsize;
 	unsigned long	chipsize;
 	int		pagemask;
 	int		pagemask;
 	int		pagebuf;
 	int		pagebuf;
+	int		subpagesize;
+	uint8_t		cellinfo;
 	int		badblockpos;
 	int		badblockpos;
 
 
 	nand_state_t	state;
 	nand_state_t	state;

+ 7 - 1
include/linux/mtd/onenand.h

@@ -13,6 +13,7 @@
 #define __LINUX_MTD_ONENAND_H
 #define __LINUX_MTD_ONENAND_H
 
 
 #include <linux/spinlock.h>
 #include <linux/spinlock.h>
+#include <linux/completion.h>
 #include <linux/mtd/onenand_regs.h>
 #include <linux/mtd/onenand_regs.h>
 #include <linux/mtd/bbm.h>
 #include <linux/mtd/bbm.h>
 
 
@@ -33,7 +34,6 @@ typedef enum {
 	FL_WRITING,
 	FL_WRITING,
 	FL_ERASING,
 	FL_ERASING,
 	FL_SYNCING,
 	FL_SYNCING,
-	FL_UNLOCKING,
 	FL_LOCKING,
 	FL_LOCKING,
 	FL_RESETING,
 	FL_RESETING,
 	FL_OTPING,
 	FL_OTPING,
@@ -88,6 +88,7 @@ struct onenand_bufferram {
  *			operation is in progress
  *			operation is in progress
  * @state:		[INTERN] the current state of the OneNAND device
  * @state:		[INTERN] the current state of the OneNAND device
  * @page_buf:		data buffer
  * @page_buf:		data buffer
+ * @subpagesize:	[INTERN] holds the subpagesize
  * @ecclayout:		[REPLACEABLE] the default ecc placement scheme
  * @ecclayout:		[REPLACEABLE] the default ecc placement scheme
  * @bbm:		[REPLACEABLE] pointer to Bad Block Management
  * @bbm:		[REPLACEABLE] pointer to Bad Block Management
  * @priv:		[OPTIONAL] pointer to private chip date
  * @priv:		[OPTIONAL] pointer to private chip date
@@ -120,11 +121,15 @@ struct onenand_chip {
 	int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
 	int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
 	int (*scan_bbt)(struct mtd_info *mtd);
 	int (*scan_bbt)(struct mtd_info *mtd);
 
 
+	struct completion	complete;
+	int			irq;
+
 	spinlock_t		chip_lock;
 	spinlock_t		chip_lock;
 	wait_queue_head_t	wq;
 	wait_queue_head_t	wq;
 	onenand_state_t		state;
 	onenand_state_t		state;
 	unsigned char		*page_buf;
 	unsigned char		*page_buf;
 
 
+	int			subpagesize;
 	struct nand_ecclayout	*ecclayout;
 	struct nand_ecclayout	*ecclayout;
 
 
 	void			*bbm;
 	void			*bbm;
@@ -138,6 +143,7 @@ struct onenand_chip {
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
 #define ONENAND_SET_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_GET_SYS_CFG1(this)					\
 #define ONENAND_GET_SYS_CFG1(this)					\
 	(this->read_word(this->base + ONENAND_REG_SYS_CFG1))
 	(this->read_word(this->base + ONENAND_REG_SYS_CFG1))

+ 1 - 0
include/linux/mtd/onenand_regs.h

@@ -179,6 +179,7 @@
  * ECC Status Reigser FF00h (R)
  * ECC Status Reigser FF00h (R)
  */
  */
 #define ONENAND_ECC_1BIT		(1 << 0)
 #define ONENAND_ECC_1BIT		(1 << 0)
+#define ONENAND_ECC_1BIT_ALL		(0x5555)
 #define ONENAND_ECC_2BIT		(1 << 1)
 #define ONENAND_ECC_2BIT		(1 << 1)
 #define ONENAND_ECC_2BIT_ALL		(0xAAAA)
 #define ONENAND_ECC_2BIT_ALL		(0xAAAA)