Browse Source

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

* git://git.infradead.org/mtd-2.6: (199 commits)
  [MTD] NAND: Fix breakage all over the place
  [PATCH] NAND: fix remaining OOB length calculation
  [MTD] NAND Fixup NDFC merge brokeness
  [MTD NAND] S3C2410 driver cleanup
  [MTD NAND] s3c24x0 board: Fix clock handling, ensure proper initialisation.
  [JFFS2] Check CRC32 on dirent and data nodes each time they're read
  [JFFS2] When retiring nextblock, allocate a node_ref for the wasted space
  [JFFS2] Mark XATTR support as experimental, for now
  [JFFS2] Don't trust node headers before the CRC is checked.
  [MTD] Restore MTD_ROM and MTD_RAM types
  [MTD] assume mtd->writesize is 1 for NOR flashes
  [MTD NAND] Fix s3c2410 NAND driver so it at least _looks_ like it compiles
  [MTD] Prepare physmap for 64-bit-resources
  [JFFS2] Fix more breakage caused by janitorial meddling.
  [JFFS2] Remove stray __exit from jffs2_compressors_exit()
  [MTD] Allow alternate JFFS2 mount variant for root filesystem.
  [MTD] Disconnect struct mtd_info from ABI
  [MTD] replace MTD_RAM with MTD_GENERIC_TYPE
  [MTD] replace MTD_ROM with MTD_GENERIC_TYPE
  [MTD] remove a forgotten MTD_XIP
  ...
Linus Torvalds 19 years ago
parent
commit
be967b7e2f
100 changed files with 8596 additions and 5708 deletions
  1. 2 2
      MAINTAINERS
  2. 2 2
      drivers/mtd/Kconfig
  3. 0 1
      drivers/mtd/chips/Kconfig
  4. 0 7
      drivers/mtd/chips/Makefile
  5. 1 7
      drivers/mtd/chips/amd_flash.c
  6. 176 298
      drivers/mtd/chips/cfi_cmdset_0001.c
  7. 2 20
      drivers/mtd/chips/cfi_cmdset_0002.c
  8. 3 19
      drivers/mtd/chips/cfi_cmdset_0020.c
  9. 4 4
      drivers/mtd/chips/cfi_probe.c
  10. 28 17
      drivers/mtd/chips/gen_probe.c
  11. 1 1
      drivers/mtd/chips/map_ram.c
  12. 1 3
      drivers/mtd/chips/map_rom.c
  13. 1 0
      drivers/mtd/chips/sharp.c
  14. 5 1
      drivers/mtd/devices/Kconfig
  15. 0 7
      drivers/mtd/devices/Makefile
  16. 20 7
      drivers/mtd/devices/block2mtd.c
  17. 29 100
      drivers/mtd/devices/doc2000.c
  18. 28 32
      drivers/mtd/devices/doc2001.c
  19. 28 32
      drivers/mtd/devices/doc2001plus.c
  20. 10 16
      drivers/mtd/devices/docprobe.c
  21. 1 0
      drivers/mtd/devices/lart.c
  22. 1 0
      drivers/mtd/devices/m25p80.c
  23. 1 1
      drivers/mtd/devices/ms02-nv.c
  24. 1 0
      drivers/mtd/devices/mtdram.c
  25. 10 6
      drivers/mtd/devices/phram.c
  26. 1 2
      drivers/mtd/devices/slram.c
  27. 132 60
      drivers/mtd/inftlcore.c
  28. 35 25
      drivers/mtd/inftlmount.c
  29. 5 6
      drivers/mtd/maps/Kconfig
  30. 2 2
      drivers/mtd/maps/cfi_flagadm.c
  31. 1 1
      drivers/mtd/maps/dbox2-flash.c
  32. 1 1
      drivers/mtd/maps/mtx-1_flash.c
  33. 3 1
      drivers/mtd/maps/nettel.c
  34. 1 0
      drivers/mtd/maps/pcmciamtd.c
  35. 186 69
      drivers/mtd/maps/physmap.c
  36. 8 8
      drivers/mtd/mtdblock.c
  37. 1 3
      drivers/mtd/mtdblock_ro.c
  38. 233 94
      drivers/mtd/mtdchar.c
  39. 188 147
      drivers/mtd/mtdconcat.c
  40. 1 32
      drivers/mtd/mtdcore.c
  41. 55 116
      drivers/mtd/mtdpart.c
  42. 53 4
      drivers/mtd/nand/Kconfig
  43. 4 0
      drivers/mtd/nand/Makefile
  44. 237 0
      drivers/mtd/nand/ams-delta.c
  45. 228 93
      drivers/mtd/nand/au1550nd.c
  46. 71 54
      drivers/mtd/nand/autcpu12.c
  47. 353 0
      drivers/mtd/nand/cs553x_nand.c
  48. 261 269
      drivers/mtd/nand/diskonchip.c
  49. 44 53
      drivers/mtd/nand/edb7312.c
  50. 39 59
      drivers/mtd/nand/h1910.c
  51. 1529 1740
      drivers/mtd/nand/nand_base.c
  52. 307 195
      drivers/mtd/nand/nand_bbt.c
  53. 88 139
      drivers/mtd/nand/nand_ecc.c
  54. 94 83
      drivers/mtd/nand/nand_ids.c
  55. 17 78
      drivers/mtd/nand/nandsim.c
  56. 311 0
      drivers/mtd/nand/ndfc.c
  57. 134 122
      drivers/mtd/nand/ppchameleonevb.c
  58. 163 188
      drivers/mtd/nand/rtc_from4.c
  59. 135 117
      drivers/mtd/nand/s3c2410.c
  60. 66 80
      drivers/mtd/nand/sharpsl.c
  61. 53 48
      drivers/mtd/nand/spia.c
  62. 62 59
      drivers/mtd/nand/toto.c
  63. 206 0
      drivers/mtd/nand/ts7250.c
  64. 144 77
      drivers/mtd/nftlcore.c
  65. 57 34
      drivers/mtd/nftlmount.c
  66. 14 0
      drivers/mtd/onenand/Kconfig
  67. 479 239
      drivers/mtd/onenand/onenand_base.c
  68. 6 3
      drivers/mtd/onenand/onenand_bbt.c
  69. 9 9
      drivers/mtd/redboot.c
  70. 25 23
      drivers/mtd/rfd_ftl.c
  71. 38 0
      fs/Kconfig
  72. 8 7
      fs/jffs/intrep.c
  73. 3 0
      fs/jffs2/Makefile
  74. 21 0
      fs/jffs2/README.Locking
  75. 485 0
      fs/jffs2/acl.c
  76. 45 0
      fs/jffs2/acl.h
  77. 2 0
      fs/jffs2/build.c
  78. 1 1
      fs/jffs2/compr.c
  79. 2 2
      fs/jffs2/compr.h
  80. 7 7
      fs/jffs2/debug.c
  81. 6 0
      fs/jffs2/debug.h
  82. 86 35
      fs/jffs2/dir.c
  83. 22 34
      fs/jffs2/erase.c
  84. 24 11
      fs/jffs2/file.c
  85. 38 25
      fs/jffs2/fs.c
  86. 67 64
      fs/jffs2/gc.c
  87. 0 3
      fs/jffs2/histo.h
  88. 5 0
      fs/jffs2/jffs2_fs_i.h
  89. 12 1
      fs/jffs2/jffs2_fs_sb.h
  90. 120 7
      fs/jffs2/malloc.c
  91. 176 7
      fs/jffs2/nodelist.c
  92. 115 74
      fs/jffs2/nodelist.h
  93. 84 112
      fs/jffs2/nodemgmt.c
  94. 8 15
      fs/jffs2/os-linux.h
  95. 74 45
      fs/jffs2/readinode.c
  96. 302 140
      fs/jffs2/scan.c
  97. 82 0
      fs/jffs2/security.c
  98. 303 181
      fs/jffs2/summary.c
  99. 45 19
      fs/jffs2/summary.h
  100. 18 2
      fs/jffs2/super.c

+ 2 - 2
MAINTAINERS

@@ -1843,12 +1843,12 @@ S:     linux-scsi@vger.kernel.org
 W:     http://megaraid.lsilogic.com
 W:     http://megaraid.lsilogic.com
 S:     Maintained
 S:     Maintained
 
 
-MEMORY TECHNOLOGY DEVICES
+MEMORY TECHNOLOGY DEVICES (MTD)
 P:	David Woodhouse
 P:	David Woodhouse
 M:	dwmw2@infradead.org
 M:	dwmw2@infradead.org
 W:	http://www.linux-mtd.infradead.org/
 W:	http://www.linux-mtd.infradead.org/
 L:	linux-mtd@lists.infradead.org
 L:	linux-mtd@lists.infradead.org
-T:	git kernel.org:/pub/scm/linux/kernel/git/tglx/mtd-2.6.git
+T:	git git://git.infradead.org/mtd-2.6.git
 S:	Maintained
 S:	Maintained
 
 
 MICROTEK X6 SCANNER
 MICROTEK X6 SCANNER

+ 2 - 2
drivers/mtd/Kconfig

@@ -78,7 +78,7 @@ config MTD_REDBOOT_DIRECTORY_BLOCK
 	  option.
 	  option.
 
 
 	  The option specifies which Flash sectors holds the RedBoot
 	  The option specifies which Flash sectors holds the RedBoot
-	  partition table.  A zero or positive value gives an absolete
+	  partition table.  A zero or positive value gives an absolute
 	  erase block number. A negative value specifies a number of
 	  erase block number. A negative value specifies a number of
 	  sectors before the end of the device.
 	  sectors before the end of the device.
 
 
@@ -103,7 +103,7 @@ config MTD_CMDLINE_PARTS
 	bool "Command line partition table parsing"
 	bool "Command line partition table parsing"
 	depends on MTD_PARTITIONS = "y"
 	depends on MTD_PARTITIONS = "y"
 	---help---
 	---help---
-	  Allow generic configuration of the MTD paritition tables via the kernel
+	  Allow generic configuration of the MTD partition tables via the kernel
 	  command line. Multiple flash resources are supported for hardware where
 	  command line. Multiple flash resources are supported for hardware where
 	  different kinds of flash memory are available.
 	  different kinds of flash memory are available.
 
 

+ 0 - 1
drivers/mtd/chips/Kconfig

@@ -30,7 +30,6 @@ config MTD_JEDECPROBE
 
 
 config MTD_GEN_PROBE
 config MTD_GEN_PROBE
 	tristate
 	tristate
-	select OBSOLETE_INTERMODULE
 
 
 config MTD_CFI_ADV_OPTIONS
 config MTD_CFI_ADV_OPTIONS
 	bool "Flash chip driver advanced configuration options"
 	bool "Flash chip driver advanced configuration options"

+ 0 - 7
drivers/mtd/chips/Makefile

@@ -3,13 +3,6 @@
 #
 #
 # $Id: Makefile.common,v 1.5 2005/11/07 11:14:22 gleixner Exp $
 # $Id: Makefile.common,v 1.5 2005/11/07 11:14:22 gleixner Exp $
 
 
-#                       *** BIG UGLY NOTE ***
-#
-# The removal of get_module_symbol() and replacement with
-# inter_module_register() et al has introduced a link order dependency
-# here where previously there was none.  We now have to ensure that
-# the CFI command set drivers are linked before gen_probe.o
-
 obj-$(CONFIG_MTD)		+= chipreg.o
 obj-$(CONFIG_MTD)		+= chipreg.o
 obj-$(CONFIG_MTD_AMDSTD)	+= amd_flash.o
 obj-$(CONFIG_MTD_AMDSTD)	+= amd_flash.o
 obj-$(CONFIG_MTD_CFI)		+= cfi_probe.o
 obj-$(CONFIG_MTD_CFI)		+= cfi_probe.o

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

@@ -97,7 +97,6 @@ struct amd_flash_private {
 	int interleave;
 	int interleave;
 	int numchips;
 	int numchips;
 	unsigned long chipshift;
 	unsigned long chipshift;
-//	const char *im_name;
 	struct flchip chips[0];
 	struct flchip chips[0];
 };
 };
 
 
@@ -131,12 +130,6 @@ static struct mtd_chip_driver amd_flash_chipdrv = {
 	.module = THIS_MODULE
 	.module = THIS_MODULE
 };
 };
 
 
-
-
-static const char im_name[] = "amd_flash";
-
-
-
 static inline __u32 wide_read(struct map_info *map, __u32 addr)
 static inline __u32 wide_read(struct map_info *map, __u32 addr)
 {
 {
 	if (map->buswidth == 1) {
 	if (map->buswidth == 1) {
@@ -737,6 +730,7 @@ static struct mtd_info *amd_flash_probe(struct map_info *map)
 		offset += dev_size;
 		offset += dev_size;
 	}
 	}
 	mtd->type = MTD_NORFLASH;
 	mtd->type = MTD_NORFLASH;
+	mtd->writesize = 1;
 	mtd->flags = MTD_CAP_NORFLASH;
 	mtd->flags = MTD_CAP_NORFLASH;
 	mtd->name = map->name;
 	mtd->name = map->name;
 	mtd->erase = amd_flash_erase;
 	mtd->erase = amd_flash_erase;

+ 176 - 298
drivers/mtd/chips/cfi_cmdset_0001.c

@@ -331,13 +331,6 @@ read_pri_intelext(struct map_info *map, __u16 adr)
 	return extp;
 	return extp;
 }
 }
 
 
-/* This routine is made available to other mtd code via
- * inter_module_register.  It must only be accessed through
- * inter_module_get which will bump the use count of this module.  The
- * addresses passed back in cfi are valid as long as the use count of
- * this module is non-zero, i.e. between inter_module_get and
- * inter_module_put.  Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
- */
 struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
 struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
 {
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_private *cfi = map->fldrv_priv;
@@ -406,7 +399,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
 	for (i=0; i< cfi->numchips; i++) {
 	for (i=0; i< cfi->numchips; i++) {
 		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
 		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
 		cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
 		cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
-		cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp;
+		cfi->chips[i].erase_time = 1000<<cfi->cfiq->BlockEraseTimeoutTyp;
 		cfi->chips[i].ref_point_counter = 0;
 		cfi->chips[i].ref_point_counter = 0;
 		init_waitqueue_head(&(cfi->chips[i].wq));
 		init_waitqueue_head(&(cfi->chips[i].wq));
 	}
 	}
@@ -415,6 +408,11 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
 
 
 	return cfi_intelext_setup(mtd);
 	return cfi_intelext_setup(mtd);
 }
 }
+struct mtd_info *cfi_cmdset_0003(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001")));
+struct mtd_info *cfi_cmdset_0200(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001")));
+EXPORT_SYMBOL_GPL(cfi_cmdset_0001);
+EXPORT_SYMBOL_GPL(cfi_cmdset_0003);
+EXPORT_SYMBOL_GPL(cfi_cmdset_0200);
 
 
 static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
 static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
 {
 {
@@ -547,12 +545,12 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
 		if (extp->MinorVersion >= '4') {
 		if (extp->MinorVersion >= '4') {
 			struct cfi_intelext_programming_regioninfo *prinfo;
 			struct cfi_intelext_programming_regioninfo *prinfo;
 			prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];
 			prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];
-			MTD_PROGREGION_SIZE(mtd) = cfi->interleave << prinfo->ProgRegShift;
+			mtd->writesize = cfi->interleave << prinfo->ProgRegShift;
 			MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid;
 			MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid;
 			MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid;
 			MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid;
-			mtd->flags |= MTD_PROGRAM_REGIONS;
+			mtd->flags &= ~MTD_BIT_WRITEABLE;
 			printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",
 			printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",
-			       map->name, MTD_PROGREGION_SIZE(mtd),
+			       map->name, mtd->writesize,
 			       MTD_PROGREGION_CTRLMODE_VALID(mtd),
 			       MTD_PROGREGION_CTRLMODE_VALID(mtd),
 			       MTD_PROGREGION_CTRLMODE_INVALID(mtd));
 			       MTD_PROGREGION_CTRLMODE_INVALID(mtd));
 		}
 		}
@@ -896,26 +894,33 @@ static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
 
 
 /*
 /*
  * When a delay is required for the flash operation to complete, the
  * When a delay is required for the flash operation to complete, the
- * xip_udelay() function is polling for both the given timeout and pending
- * (but still masked) hardware interrupts.  Whenever there is an interrupt
- * pending then the flash erase or write operation is suspended, array mode
- * restored and interrupts unmasked.  Task scheduling might also happen at that
- * point.  The CPU eventually returns from the interrupt or the call to
- * schedule() and the suspended flash operation is resumed for the remaining
- * of the delay period.
+ * xip_wait_for_operation() function is polling for both the given timeout
+ * and pending (but still masked) hardware interrupts.  Whenever there is an
+ * interrupt pending then the flash erase or write operation is suspended,
+ * array mode restored and interrupts unmasked.  Task scheduling might also
+ * happen at that point.  The CPU eventually returns from the interrupt or
+ * the call to schedule() and the suspended flash operation is resumed for
+ * the remaining of the delay period.
  *
  *
  * Warning: this function _will_ fool interrupt latency tracing tools.
  * Warning: this function _will_ fool interrupt latency tracing tools.
  */
  */
 
 
-static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
-				unsigned long adr, int usec)
+static int __xipram xip_wait_for_operation(
+		struct map_info *map, struct flchip *chip,
+		unsigned long adr, int *chip_op_time )
 {
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
 	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
 	map_word status, OK = CMD(0x80);
 	map_word status, OK = CMD(0x80);
-	unsigned long suspended, start = xip_currtime();
+	unsigned long usec, suspended, start, done;
 	flstate_t oldstate, newstate;
 	flstate_t oldstate, newstate;
 
 
+       	start = xip_currtime();
+	usec = *chip_op_time * 8;
+	if (usec == 0)
+		usec = 500000;
+	done = 0;
+
 	do {
 	do {
 		cpu_relax();
 		cpu_relax();
 		if (xip_irqpending() && cfip &&
 		if (xip_irqpending() && cfip &&
@@ -932,9 +937,9 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
 			 * we resume the whole thing at once).  Yes, it
 			 * we resume the whole thing at once).  Yes, it
 			 * can happen!
 			 * can happen!
 			 */
 			 */
+			usec -= done;
 			map_write(map, CMD(0xb0), adr);
 			map_write(map, CMD(0xb0), adr);
 			map_write(map, CMD(0x70), adr);
 			map_write(map, CMD(0x70), adr);
-			usec -= xip_elapsed_since(start);
 			suspended = xip_currtime();
 			suspended = xip_currtime();
 			do {
 			do {
 				if (xip_elapsed_since(suspended) > 100000) {
 				if (xip_elapsed_since(suspended) > 100000) {
@@ -944,7 +949,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
 					 * This is a critical error but there
 					 * This is a critical error but there
 					 * is not much we can do here.
 					 * is not much we can do here.
 					 */
 					 */
-					return;
+					return -EIO;
 				}
 				}
 				status = map_read(map, adr);
 				status = map_read(map, adr);
 			} while (!map_word_andequal(map, status, OK, OK));
 			} while (!map_word_andequal(map, status, OK, OK));
@@ -1004,65 +1009,107 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
 			xip_cpu_idle();
 			xip_cpu_idle();
 		}
 		}
 		status = map_read(map, adr);
 		status = map_read(map, adr);
+		done = xip_elapsed_since(start);
 	} while (!map_word_andequal(map, status, OK, OK)
 	} while (!map_word_andequal(map, status, OK, OK)
-		 && xip_elapsed_since(start) < usec);
-}
+		 && done < usec);
 
 
-#define UDELAY(map, chip, adr, usec)  xip_udelay(map, chip, adr, usec)
+	return (done >= usec) ? -ETIME : 0;
+}
 
 
 /*
 /*
  * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
  * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
  * the flash is actively programming or erasing since we have to poll for
  * the flash is actively programming or erasing since we have to poll for
  * the operation to complete anyway.  We can't do that in a generic way with
  * the operation to complete anyway.  We can't do that in a generic way with
  * a XIP setup so do it before the actual flash operation in this case
  * a XIP setup so do it before the actual flash operation in this case
- * and stub it out from INVALIDATE_CACHE_UDELAY.
+ * and stub it out from INVAL_CACHE_AND_WAIT.
  */
  */
 #define XIP_INVAL_CACHED_RANGE(map, from, size)  \
 #define XIP_INVAL_CACHED_RANGE(map, from, size)  \
 	INVALIDATE_CACHED_RANGE(map, from, size)
 	INVALIDATE_CACHED_RANGE(map, from, size)
 
 
-#define INVALIDATE_CACHE_UDELAY(map, chip, cmd_adr, adr, len, usec)  \
-	UDELAY(map, chip, cmd_adr, usec)
-
-/*
- * Extra notes:
- *
- * Activating this XIP support changes the way the code works a bit.  For
- * example the code to suspend the current process when concurrent access
- * happens is never executed because xip_udelay() will always return with the
- * same chip state as it was entered with.  This is why there is no care for
- * the presence of add_wait_queue() or schedule() calls from within a couple
- * xip_disable()'d  areas of code, like in do_erase_oneblock for example.
- * The queueing and scheduling are always happening within xip_udelay().
- *
- * Similarly, get_chip() and put_chip() just happen to always be executed
- * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
- * is in array mode, therefore never executing many cases therein and not
- * causing any problem with XIP.
- */
+#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, p_usec) \
+	xip_wait_for_operation(map, chip, cmd_adr, p_usec)
 
 
 #else
 #else
 
 
 #define xip_disable(map, chip, adr)
 #define xip_disable(map, chip, adr)
 #define xip_enable(map, chip, adr)
 #define xip_enable(map, chip, adr)
 #define XIP_INVAL_CACHED_RANGE(x...)
 #define XIP_INVAL_CACHED_RANGE(x...)
+#define INVAL_CACHE_AND_WAIT inval_cache_and_wait_for_operation
+
+static int inval_cache_and_wait_for_operation(
+		struct map_info *map, struct flchip *chip,
+		unsigned long cmd_adr, unsigned long inval_adr, int inval_len,
+		int *chip_op_time )
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	map_word status, status_OK = CMD(0x80);
+	int z, chip_state = chip->state;
+	unsigned long timeo;
+
+	spin_unlock(chip->mutex);
+	if (inval_len)
+		INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len);
+	if (*chip_op_time)
+		cfi_udelay(*chip_op_time);
+	spin_lock(chip->mutex);
+
+	timeo = *chip_op_time * 8 * HZ / 1000000;
+	if (timeo < HZ/2)
+		timeo = HZ/2;
+	timeo += jiffies;
+
+	z = 0;
+	for (;;) {
+		if (chip->state != chip_state) {
+			/* Someone's suspended the operation: sleep */
+			DECLARE_WAITQUEUE(wait, current);
+
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			add_wait_queue(&chip->wq, &wait);
+			spin_unlock(chip->mutex);
+			schedule();
+			remove_wait_queue(&chip->wq, &wait);
+			timeo = jiffies + (HZ / 2); /* FIXME */
+			spin_lock(chip->mutex);
+			continue;
+		}
 
 
-#define UDELAY(map, chip, adr, usec)  \
-do {  \
-	spin_unlock(chip->mutex);  \
-	cfi_udelay(usec);  \
-	spin_lock(chip->mutex);  \
-} while (0)
-
-#define INVALIDATE_CACHE_UDELAY(map, chip, cmd_adr, adr, len, usec)  \
-do {  \
-	spin_unlock(chip->mutex);  \
-	INVALIDATE_CACHED_RANGE(map, adr, len);  \
-	cfi_udelay(usec);  \
-	spin_lock(chip->mutex);  \
-} while (0)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
+			break;
+
+		/* OK Still waiting */
+		if (time_after(jiffies, timeo)) {
+			map_write(map, CMD(0x70), cmd_adr);
+			chip->state = FL_STATUS;
+			return -ETIME;
+		}
+
+		/* Latency issues. Drop the lock, wait a while and retry */
+		z++;
+		spin_unlock(chip->mutex);
+		cfi_udelay(1);
+		spin_lock(chip->mutex);
+	}
+
+	if (!z) {
+		if (!--(*chip_op_time))
+			*chip_op_time = 1;
+	} else if (z > 1)
+		++(*chip_op_time);
+
+	/* Done and happy. */
+ 	chip->state = FL_STATUS;
+	return 0;
+}
 
 
 #endif
 #endif
 
 
+#define WAIT_TIMEOUT(map, chip, adr, udelay) \
+	({ int __udelay = (udelay); \
+	   INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, &__udelay); })
+
+
 static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
 static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
 {
 {
 	unsigned long cmd_addr;
 	unsigned long cmd_addr;
@@ -1252,14 +1299,11 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
 				     unsigned long adr, map_word datum, int mode)
 				     unsigned long adr, map_word datum, int mode)
 {
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	map_word status, status_OK, write_cmd;
-	unsigned long timeo;
-	int z, ret=0;
+	map_word status, write_cmd;
+	int ret=0;
 
 
 	adr += chip->start;
 	adr += chip->start;
 
 
-	/* Let's determine those according to the interleave only once */
-	status_OK = CMD(0x80);
 	switch (mode) {
 	switch (mode) {
 	case FL_WRITING:
 	case FL_WRITING:
 		write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41);
 		write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41);
@@ -1285,57 +1329,17 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
 	map_write(map, datum, adr);
 	map_write(map, datum, adr);
 	chip->state = mode;
 	chip->state = mode;
 
 
-	INVALIDATE_CACHE_UDELAY(map, chip, adr,
-				adr, map_bankwidth(map),
-				chip->word_write_time);
-
-	timeo = jiffies + (HZ/2);
-	z = 0;
-	for (;;) {
-		if (chip->state != mode) {
-			/* Someone's suspended the write. Sleep */
-			DECLARE_WAITQUEUE(wait, current);
-
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			add_wait_queue(&chip->wq, &wait);
-			spin_unlock(chip->mutex);
-			schedule();
-			remove_wait_queue(&chip->wq, &wait);
-			timeo = jiffies + (HZ / 2); /* FIXME */
-			spin_lock(chip->mutex);
-			continue;
-		}
-
-		status = map_read(map, adr);
-		if (map_word_andequal(map, status, status_OK, status_OK))
-			break;
-
-		/* OK Still waiting */
-		if (time_after(jiffies, timeo)) {
-			map_write(map, CMD(0x70), adr);
-			chip->state = FL_STATUS;
-			xip_enable(map, chip, adr);
-			printk(KERN_ERR "%s: word write error (status timeout)\n", map->name);
-			ret = -EIO;
-			goto out;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		z++;
-		UDELAY(map, chip, adr, 1);
-	}
-	if (!z) {
-		chip->word_write_time--;
-		if (!chip->word_write_time)
-			chip->word_write_time = 1;
+	ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
+				   adr, map_bankwidth(map),
+				   &chip->word_write_time);
+	if (ret) {
+		xip_enable(map, chip, adr);
+		printk(KERN_ERR "%s: word write error (status timeout)\n", map->name);
+		goto out;
 	}
 	}
-	if (z > 1)
-		chip->word_write_time++;
-
-	/* Done and happy. */
-	chip->state = FL_STATUS;
 
 
 	/* check for errors */
 	/* check for errors */
+	status = map_read(map, adr);
 	if (map_word_bitsset(map, status, CMD(0x1a))) {
 	if (map_word_bitsset(map, status, CMD(0x1a))) {
 		unsigned long chipstatus = MERGESTATUS(status);
 		unsigned long chipstatus = MERGESTATUS(status);
 
 
@@ -1452,9 +1456,9 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
 				    unsigned long *pvec_seek, int len)
 				    unsigned long *pvec_seek, int len)
 {
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	map_word status, status_OK, write_cmd, datum;
-	unsigned long cmd_adr, timeo;
-	int wbufsize, z, ret=0, word_gap, words;
+	map_word status, write_cmd, datum;
+	unsigned long cmd_adr;
+	int ret, wbufsize, word_gap, words;
 	const struct kvec *vec;
 	const struct kvec *vec;
 	unsigned long vec_seek;
 	unsigned long vec_seek;
 
 
@@ -1463,7 +1467,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
 	cmd_adr = adr & ~(wbufsize-1);
 	cmd_adr = adr & ~(wbufsize-1);
 
 
 	/* Let's determine this according to the interleave only once */
 	/* Let's determine this according to the interleave only once */
-	status_OK = CMD(0x80);
 	write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9);
 	write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9);
 
 
 	spin_lock(chip->mutex);
 	spin_lock(chip->mutex);
@@ -1477,12 +1480,14 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
 	ENABLE_VPP(map);
 	ENABLE_VPP(map);
 	xip_disable(map, chip, cmd_adr);
 	xip_disable(map, chip, cmd_adr);
 
 
-	/* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
+	/* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
 	   [...], the device will not accept any more Write to Buffer commands".
 	   [...], the device will not accept any more Write to Buffer commands".
 	   So we must check here and reset those bits if they're set. Otherwise
 	   So we must check here and reset those bits if they're set. Otherwise
 	   we're just pissing in the wind */
 	   we're just pissing in the wind */
-	if (chip->state != FL_STATUS)
+	if (chip->state != FL_STATUS) {
 		map_write(map, CMD(0x70), cmd_adr);
 		map_write(map, CMD(0x70), cmd_adr);
+		chip->state = FL_STATUS;
+	}
 	status = map_read(map, cmd_adr);
 	status = map_read(map, cmd_adr);
 	if (map_word_bitsset(map, status, CMD(0x30))) {
 	if (map_word_bitsset(map, status, CMD(0x30))) {
 		xip_enable(map, chip, cmd_adr);
 		xip_enable(map, chip, cmd_adr);
@@ -1493,32 +1498,20 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
 	}
 	}
 
 
 	chip->state = FL_WRITING_TO_BUFFER;
 	chip->state = FL_WRITING_TO_BUFFER;
-
-	z = 0;
-	for (;;) {
-		map_write(map, write_cmd, cmd_adr);
-
+	map_write(map, write_cmd, cmd_adr);
+	ret = WAIT_TIMEOUT(map, chip, cmd_adr, 0);
+	if (ret) {
+		/* Argh. Not ready for write to buffer */
+		map_word Xstatus = map_read(map, cmd_adr);
+		map_write(map, CMD(0x70), cmd_adr);
+		chip->state = FL_STATUS;
 		status = map_read(map, cmd_adr);
 		status = map_read(map, cmd_adr);
-		if (map_word_andequal(map, status, status_OK, status_OK))
-			break;
-
-		UDELAY(map, chip, cmd_adr, 1);
-
-		if (++z > 20) {
-			/* Argh. Not ready for write to buffer */
-			map_word Xstatus;
-			map_write(map, CMD(0x70), cmd_adr);
-			chip->state = FL_STATUS;
-			Xstatus = map_read(map, cmd_adr);
-			/* Odd. Clear status bits */
-			map_write(map, CMD(0x50), cmd_adr);
-			map_write(map, CMD(0x70), cmd_adr);
-			xip_enable(map, chip, cmd_adr);
-			printk(KERN_ERR "%s: Chip not ready for buffer write. status = %lx, Xstatus = %lx\n",
-			       map->name, status.x[0], Xstatus.x[0]);
-			ret = -EIO;
-			goto out;
-		}
+		map_write(map, CMD(0x50), cmd_adr);
+		map_write(map, CMD(0x70), cmd_adr);
+		xip_enable(map, chip, cmd_adr);
+		printk(KERN_ERR "%s: Chip not ready for buffer write. Xstatus = %lx, status = %lx\n",
+				map->name, Xstatus.x[0], status.x[0]);
+		goto out;
 	}
 	}
 
 
 	/* Figure out the number of words to write */
 	/* Figure out the number of words to write */
@@ -1573,56 +1566,19 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
 	map_write(map, CMD(0xd0), cmd_adr);
 	map_write(map, CMD(0xd0), cmd_adr);
 	chip->state = FL_WRITING;
 	chip->state = FL_WRITING;
 
 
-	INVALIDATE_CACHE_UDELAY(map, chip, cmd_adr,
-				adr, len,
-				chip->buffer_write_time);
-
-	timeo = jiffies + (HZ/2);
-	z = 0;
-	for (;;) {
-		if (chip->state != FL_WRITING) {
-			/* Someone's suspended the write. Sleep */
-			DECLARE_WAITQUEUE(wait, current);
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			add_wait_queue(&chip->wq, &wait);
-			spin_unlock(chip->mutex);
-			schedule();
-			remove_wait_queue(&chip->wq, &wait);
-			timeo = jiffies + (HZ / 2); /* FIXME */
-			spin_lock(chip->mutex);
-			continue;
-		}
-
-		status = map_read(map, cmd_adr);
-		if (map_word_andequal(map, status, status_OK, status_OK))
-			break;
-
-		/* OK Still waiting */
-		if (time_after(jiffies, timeo)) {
-			map_write(map, CMD(0x70), cmd_adr);
-			chip->state = FL_STATUS;
-			xip_enable(map, chip, cmd_adr);
-			printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name);
-			ret = -EIO;
-			goto out;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		z++;
-		UDELAY(map, chip, cmd_adr, 1);
-	}
-	if (!z) {
-		chip->buffer_write_time--;
-		if (!chip->buffer_write_time)
-			chip->buffer_write_time = 1;
+	ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr,
+				   adr, len,
+				   &chip->buffer_write_time);
+	if (ret) {
+		map_write(map, CMD(0x70), cmd_adr);
+		chip->state = FL_STATUS;
+		xip_enable(map, chip, cmd_adr);
+		printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name);
+		goto out;
 	}
 	}
-	if (z > 1)
-		chip->buffer_write_time++;
-
-	/* Done and happy. */
- 	chip->state = FL_STATUS;
 
 
 	/* check for errors */
 	/* check for errors */
+	status = map_read(map, cmd_adr);
 	if (map_word_bitsset(map, status, CMD(0x1a))) {
 	if (map_word_bitsset(map, status, CMD(0x1a))) {
 		unsigned long chipstatus = MERGESTATUS(status);
 		unsigned long chipstatus = MERGESTATUS(status);
 
 
@@ -1693,6 +1649,11 @@ static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,
 			if (chipnum == cfi->numchips)
 			if (chipnum == cfi->numchips)
 				return 0;
 				return 0;
 		}
 		}
+
+		/* Be nice and reschedule with the chip in a usable state for other
+		   processes. */
+		cond_resched();
+
 	} while (len);
 	} while (len);
 
 
 	return 0;
 	return 0;
@@ -1713,17 +1674,12 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 				      unsigned long adr, int len, void *thunk)
 				      unsigned long adr, int len, void *thunk)
 {
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	map_word status, status_OK;
-	unsigned long timeo;
+	map_word status;
 	int retries = 3;
 	int retries = 3;
-	DECLARE_WAITQUEUE(wait, current);
-	int ret = 0;
+	int ret;
 
 
 	adr += chip->start;
 	adr += chip->start;
 
 
-	/* Let's determine this according to the interleave only once */
-	status_OK = CMD(0x80);
-
  retry:
  retry:
 	spin_lock(chip->mutex);
 	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, adr, FL_ERASING);
 	ret = get_chip(map, chip, adr, FL_ERASING);
@@ -1745,48 +1701,15 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 	chip->state = FL_ERASING;
 	chip->state = FL_ERASING;
 	chip->erase_suspended = 0;
 	chip->erase_suspended = 0;
 
 
-	INVALIDATE_CACHE_UDELAY(map, chip, adr,
-				adr, len,
-				chip->erase_time*1000/2);
-
-	/* FIXME. Use a timer to check this, and return immediately. */
-	/* Once the state machine's known to be working I'll do that */
-
-	timeo = jiffies + (HZ*20);
-	for (;;) {
-		if (chip->state != FL_ERASING) {
-			/* Someone's suspended the erase. Sleep */
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			add_wait_queue(&chip->wq, &wait);
-			spin_unlock(chip->mutex);
-			schedule();
-			remove_wait_queue(&chip->wq, &wait);
-			spin_lock(chip->mutex);
-			continue;
-		}
-		if (chip->erase_suspended) {
-			/* This erase was suspended and resumed.
-			   Adjust the timeout */
-			timeo = jiffies + (HZ*20); /* FIXME */
-			chip->erase_suspended = 0;
-		}
-
-		status = map_read(map, adr);
-		if (map_word_andequal(map, status, status_OK, status_OK))
-			break;
-
-		/* OK Still waiting */
-		if (time_after(jiffies, timeo)) {
-			map_write(map, CMD(0x70), adr);
-			chip->state = FL_STATUS;
-			xip_enable(map, chip, adr);
-			printk(KERN_ERR "%s: block erase error: (status timeout)\n", map->name);
-			ret = -EIO;
-			goto out;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		UDELAY(map, chip, adr, 1000000/HZ);
+	ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
+				   adr, len,
+				   &chip->erase_time);
+	if (ret) {
+		map_write(map, CMD(0x70), adr);
+		chip->state = FL_STATUS;
+		xip_enable(map, chip, adr);
+		printk(KERN_ERR "%s: block erase error: (status timeout)\n", map->name);
+		goto out;
 	}
 	}
 
 
 	/* We've broken this before. It doesn't hurt to be safe */
 	/* We've broken this before. It doesn't hurt to be safe */
@@ -1815,7 +1738,6 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 			ret = -EIO;
 			ret = -EIO;
 		} else if (chipstatus & 0x20 && retries--) {
 		} else if (chipstatus & 0x20 && retries--) {
 			printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
 			printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
-			timeo = jiffies + HZ;
 			put_chip(map, chip, adr);
 			put_chip(map, chip, adr);
 			spin_unlock(chip->mutex);
 			spin_unlock(chip->mutex);
 			goto retry;
 			goto retry;
@@ -1921,15 +1843,11 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
 {
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
-	map_word status, status_OK;
-	unsigned long timeo = jiffies + HZ;
+	int udelay;
 	int ret;
 	int ret;
 
 
 	adr += chip->start;
 	adr += chip->start;
 
 
-	/* Let's determine this according to the interleave only once */
-	status_OK = CMD(0x80);
-
 	spin_lock(chip->mutex);
 	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, adr, FL_LOCKING);
 	ret = get_chip(map, chip, adr, FL_LOCKING);
 	if (ret) {
 	if (ret) {
@@ -1954,41 +1872,21 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
 	 * If Instant Individual Block Locking supported then no need
 	 * If Instant Individual Block Locking supported then no need
 	 * to delay.
 	 * to delay.
 	 */
 	 */
+	udelay = (!extp || !(extp->FeatureSupport & (1 << 5))) ? 1000000/HZ : 0;
 
 
-	if (!extp || !(extp->FeatureSupport & (1 << 5)))
-		UDELAY(map, chip, adr, 1000000/HZ);
-
-	/* FIXME. Use a timer to check this, and return immediately. */
-	/* Once the state machine's known to be working I'll do that */
-
-	timeo = jiffies + (HZ*20);
-	for (;;) {
-
-		status = map_read(map, adr);
-		if (map_word_andequal(map, status, status_OK, status_OK))
-			break;
-
-		/* OK Still waiting */
-		if (time_after(jiffies, timeo)) {
-			map_write(map, CMD(0x70), adr);
-			chip->state = FL_STATUS;
-			xip_enable(map, chip, adr);
-			printk(KERN_ERR "%s: block unlock error: (status timeout)\n", map->name);
-			put_chip(map, chip, adr);
-			spin_unlock(chip->mutex);
-			return -EIO;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		UDELAY(map, chip, adr, 1);
+	ret = WAIT_TIMEOUT(map, chip, adr, udelay);
+	if (ret) {
+		map_write(map, CMD(0x70), adr);
+		chip->state = FL_STATUS;
+		xip_enable(map, chip, adr);
+		printk(KERN_ERR "%s: block unlock error: (status timeout)\n", map->name);
+		goto out;
 	}
 	}
 
 
-	/* Done and happy. */
-	chip->state = FL_STATUS;
 	xip_enable(map, chip, adr);
 	xip_enable(map, chip, adr);
-	put_chip(map, chip, adr);
+out:	put_chip(map, chip, adr);
 	spin_unlock(chip->mutex);
 	spin_unlock(chip->mutex);
-	return 0;
+	return ret;
 }
 }
 
 
 static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
 static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
@@ -2445,28 +2343,8 @@ static void cfi_intelext_destroy(struct mtd_info *mtd)
 	kfree(mtd->eraseregions);
 	kfree(mtd->eraseregions);
 }
 }
 
 
-static char im_name_0001[] = "cfi_cmdset_0001";
-static char im_name_0003[] = "cfi_cmdset_0003";
-static char im_name_0200[] = "cfi_cmdset_0200";
-
-static int __init cfi_intelext_init(void)
-{
-	inter_module_register(im_name_0001, THIS_MODULE, &cfi_cmdset_0001);
-	inter_module_register(im_name_0003, THIS_MODULE, &cfi_cmdset_0001);
-	inter_module_register(im_name_0200, THIS_MODULE, &cfi_cmdset_0001);
-	return 0;
-}
-
-static void __exit cfi_intelext_exit(void)
-{
-	inter_module_unregister(im_name_0001);
-	inter_module_unregister(im_name_0003);
-	inter_module_unregister(im_name_0200);
-}
-
-module_init(cfi_intelext_init);
-module_exit(cfi_intelext_exit);
-
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
 MODULE_DESCRIPTION("MTD chip driver for Intel/Sharp flash chips");
 MODULE_DESCRIPTION("MTD chip driver for Intel/Sharp flash chips");
+MODULE_ALIAS("cfi_cmdset_0003");
+MODULE_ALIAS("cfi_cmdset_0200");

+ 2 - 20
drivers/mtd/chips/cfi_cmdset_0002.c

@@ -236,6 +236,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 	mtd->resume  = cfi_amdstd_resume;
 	mtd->resume  = cfi_amdstd_resume;
 	mtd->flags   = MTD_CAP_NORFLASH;
 	mtd->flags   = MTD_CAP_NORFLASH;
 	mtd->name    = map->name;
 	mtd->name    = map->name;
+	mtd->writesize = 1;
 
 
 	if (cfi->cfi_mode==CFI_MODE_CFI){
 	if (cfi->cfi_mode==CFI_MODE_CFI){
 		unsigned char bootloc;
 		unsigned char bootloc;
@@ -326,7 +327,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 
 
 	return cfi_amdstd_setup(mtd);
 	return cfi_amdstd_setup(mtd);
 }
 }
-
+EXPORT_SYMBOL_GPL(cfi_cmdset_0002);
 
 
 static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
 static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
 {
 {
@@ -1758,25 +1759,6 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd)
 	kfree(mtd->eraseregions);
 	kfree(mtd->eraseregions);
 }
 }
 
 
-static char im_name[]="cfi_cmdset_0002";
-
-
-static int __init cfi_amdstd_init(void)
-{
-	inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002);
-	return 0;
-}
-
-
-static void __exit cfi_amdstd_exit(void)
-{
-	inter_module_unregister(im_name);
-}
-
-
-module_init(cfi_amdstd_init);
-module_exit(cfi_amdstd_exit);
-
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
 MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
 MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");
 MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");

+ 3 - 19
drivers/mtd/chips/cfi_cmdset_0020.c

@@ -162,6 +162,7 @@ struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary)
 
 
 	return cfi_staa_setup(map);
 	return cfi_staa_setup(map);
 }
 }
+EXPORT_SYMBOL_GPL(cfi_cmdset_0020);
 
 
 static struct mtd_info *cfi_staa_setup(struct map_info *map)
 static struct mtd_info *cfi_staa_setup(struct map_info *map)
 {
 {
@@ -237,9 +238,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
 	mtd->unlock = cfi_staa_unlock;
 	mtd->unlock = cfi_staa_unlock;
 	mtd->suspend = cfi_staa_suspend;
 	mtd->suspend = cfi_staa_suspend;
 	mtd->resume = cfi_staa_resume;
 	mtd->resume = cfi_staa_resume;
-	mtd->flags = MTD_CAP_NORFLASH;
-	mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */
-	mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
+	mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE;
+	mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
 	map->fldrv = &cfi_staa_chipdrv;
 	map->fldrv = &cfi_staa_chipdrv;
 	__module_get(THIS_MODULE);
 	__module_get(THIS_MODULE);
 	mtd->name = map->name;
 	mtd->name = map->name;
@@ -1410,20 +1410,4 @@ static void cfi_staa_destroy(struct mtd_info *mtd)
 	kfree(cfi);
 	kfree(cfi);
 }
 }
 
 
-static char im_name[]="cfi_cmdset_0020";
-
-static int __init cfi_staa_init(void)
-{
-	inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020);
-	return 0;
-}
-
-static void __exit cfi_staa_exit(void)
-{
-	inter_module_unregister(im_name);
-}
-
-module_init(cfi_staa_init);
-module_exit(cfi_staa_exit);
-
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 4 - 4
drivers/mtd/chips/cfi_probe.c

@@ -349,12 +349,12 @@ static void print_cfi_ident(struct cfi_ident *cfip)
 	else
 	else
 		printk("No Vpp line\n");
 		printk("No Vpp line\n");
 
 
-	printk("Typical byte/word write timeout: %d µs\n", 1<<cfip->WordWriteTimeoutTyp);
-	printk("Maximum byte/word write timeout: %d µs\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp));
+	printk("Typical byte/word write timeout: %d µs\n", 1<<cfip->WordWriteTimeoutTyp);
+	printk("Maximum byte/word write timeout: %d µs\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp));
 
 
 	if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) {
 	if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) {
-		printk("Typical full buffer write timeout: %d µs\n", 1<<cfip->BufWriteTimeoutTyp);
-		printk("Maximum full buffer write timeout: %d µs\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp));
+		printk("Typical full buffer write timeout: %d µs\n", 1<<cfip->BufWriteTimeoutTyp);
+		printk("Maximum full buffer write timeout: %d µs\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp));
 	}
 	}
 	else
 	else
 		printk("Full buffer write not supported\n");
 		printk("Full buffer write not supported\n");

+ 28 - 17
drivers/mtd/chips/gen_probe.c

@@ -37,8 +37,15 @@ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
 	if (!mtd)
 	if (!mtd)
 		mtd = check_cmd_set(map, 0); /* Then the secondary */
 		mtd = check_cmd_set(map, 0); /* Then the secondary */
 
 
-	if (mtd)
+	if (mtd) {
+		if (mtd->size > map->size) {
+			printk(KERN_WARNING "Reducing visibility of %ldKiB chip to %ldKiB\n",
+			       (unsigned long)mtd->size >> 10, 
+			       (unsigned long)map->size >> 10);
+			mtd->size = map->size;
+		}
 		return mtd;
 		return mtd;
+	}
 
 
 	printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n");
 	printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n");
 
 
@@ -100,7 +107,12 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
 	 * Align bitmap storage size to full byte.
 	 * Align bitmap storage size to full byte.
 	 */
 	 */
 	max_chips = map->size >> cfi.chipshift;
 	max_chips = map->size >> cfi.chipshift;
-	mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0);
+	if (!max_chips) {
+		printk(KERN_WARNING "NOR chip too large to fit in mapping. Attempting to cope...\n");
+		max_chips = 1;
+	}
+
+	mapsize = (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG;
 	chip_map = kmalloc(mapsize, GFP_KERNEL);
 	chip_map = kmalloc(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);
@@ -194,25 +206,28 @@ static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map,
 {
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
 	__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
-#if defined(CONFIG_MODULES) && defined(HAVE_INTER_MODULE)
-	char probename[32];
+#ifdef CONFIG_MODULES
+	char probename[16+sizeof(MODULE_SYMBOL_PREFIX)];
 	cfi_cmdset_fn_t *probe_function;
 	cfi_cmdset_fn_t *probe_function;
 
 
-	sprintf(probename, "cfi_cmdset_%4.4X", type);
+	sprintf(probename, MODULE_SYMBOL_PREFIX "cfi_cmdset_%4.4X", type);
 
 
-	probe_function = inter_module_get_request(probename, probename);
+	probe_function = __symbol_get(probename);
+	if (!probe_function) {
+		request_module(probename + sizeof(MODULE_SYMBOL_PREFIX) - 1);
+		probe_function = __symbol_get(probename);
+	}
 
 
 	if (probe_function) {
 	if (probe_function) {
 		struct mtd_info *mtd;
 		struct mtd_info *mtd;
 
 
 		mtd = (*probe_function)(map, primary);
 		mtd = (*probe_function)(map, primary);
 		/* If it was happy, it'll have increased its own use count */
 		/* If it was happy, it'll have increased its own use count */
-		inter_module_put(probename);
+		symbol_put_addr(probe_function);
 		return mtd;
 		return mtd;
 	}
 	}
 #endif
 #endif
-	printk(KERN_NOTICE "Support for command set %04X not present\n",
-	       type);
+	printk(KERN_NOTICE "Support for command set %04X not present\n", type);
 
 
 	return NULL;
 	return NULL;
 }
 }
@@ -226,12 +241,8 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
 		return NULL;
 		return NULL;
 
 
 	switch(type){
 	switch(type){
-		/* Urgh. Ifdefs. The version with weak symbols was
-		 * _much_ nicer. Shame it didn't seem to work on
-		 * anything but x86, really.
-		 * But we can't rely in inter_module_get() because
-		 * that'd mean we depend on link order.
-		 */
+		/* We need these for the !CONFIG_MODULES case,
+		   because symbol_get() doesn't work there */
 #ifdef CONFIG_MTD_CFI_INTELEXT
 #ifdef CONFIG_MTD_CFI_INTELEXT
 	case 0x0001:
 	case 0x0001:
 	case 0x0003:
 	case 0x0003:
@@ -246,9 +257,9 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
         case 0x0020:
         case 0x0020:
 		return cfi_cmdset_0020(map, primary);
 		return cfi_cmdset_0020(map, primary);
 #endif
 #endif
+	default:
+		return cfi_cmdset_unknown(map, primary);
 	}
 	}
-
-	return cfi_cmdset_unknown(map, primary);
 }
 }
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

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

@@ -70,7 +70,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
 	mtd->read = mapram_read;
 	mtd->read = mapram_read;
 	mtd->write = mapram_write;
 	mtd->write = mapram_write;
 	mtd->sync = mapram_nop;
 	mtd->sync = mapram_nop;
-	mtd->flags = MTD_CAP_RAM | MTD_VOLATILE;
+	mtd->flags = MTD_CAP_RAM;
 
 
 	mtd->erasesize = PAGE_SIZE;
 	mtd->erasesize = PAGE_SIZE;
  	while(mtd->size & (mtd->erasesize - 1))
  	while(mtd->size & (mtd->erasesize - 1))

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

@@ -46,9 +46,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
 	mtd->write = maprom_write;
 	mtd->write = maprom_write;
 	mtd->sync = maprom_nop;
 	mtd->sync = maprom_nop;
 	mtd->flags = MTD_CAP_ROM;
 	mtd->flags = MTD_CAP_ROM;
-	mtd->erasesize = 131072;
- 	while(mtd->size & (mtd->erasesize - 1))
-		mtd->erasesize >>= 1;
+	mtd->erasesize = map->size;
 
 
 	__module_get(THIS_MODULE);
 	__module_get(THIS_MODULE);
 	return mtd;
 	return mtd;

+ 1 - 0
drivers/mtd/chips/sharp.c

@@ -140,6 +140,7 @@ static struct mtd_info *sharp_probe(struct map_info *map)
 	mtd->suspend = sharp_suspend;
 	mtd->suspend = sharp_suspend;
 	mtd->resume = sharp_resume;
 	mtd->resume = sharp_resume;
 	mtd->flags = MTD_CAP_NORFLASH;
 	mtd->flags = MTD_CAP_NORFLASH;
+	mtd->writesize = 1;
 	mtd->name = map->name;
 	mtd->name = map->name;
 
 
 	memset(sharp, 0, sizeof(*sharp));
 	memset(sharp, 0, sizeof(*sharp));

+ 5 - 1
drivers/mtd/devices/Kconfig

@@ -47,6 +47,11 @@ config MTD_MS02NV
 	  accelerator.  Say Y here if you have a DECstation 5000/2x0 or a
 	  accelerator.  Say Y here if you have a DECstation 5000/2x0 or a
 	  DECsystem 5900 equipped with such a module.
 	  DECsystem 5900 equipped with such a module.
 
 
+	  If you want to compile this driver as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/modules.txt>.  The module will
+	  be called ms02-nv.o.
+
 config MTD_DATAFLASH
 config MTD_DATAFLASH
 	tristate "Support for AT45xxx DataFlash"
 	tristate "Support for AT45xxx DataFlash"
 	depends on MTD && SPI_MASTER && EXPERIMENTAL
 	depends on MTD && SPI_MASTER && EXPERIMENTAL
@@ -209,7 +214,6 @@ config MTD_DOC2001PLUS
 config MTD_DOCPROBE
 config MTD_DOCPROBE
 	tristate
 	tristate
 	select MTD_DOCECC
 	select MTD_DOCECC
-	select OBSOLETE_INTERMODULE
 
 
 config MTD_DOCECC
 config MTD_DOCECC
 	tristate
 	tristate

+ 0 - 7
drivers/mtd/devices/Makefile

@@ -3,13 +3,6 @@
 #
 #
 # $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $
 # $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $
 
 
-#                       *** BIG UGLY NOTE ***
-#
-# The removal of get_module_symbol() and replacement with
-# inter_module_register() et al has introduced a link order dependency
-# here where previously there was none.  We now have to ensure that
-# doc200[01].o are linked before docprobe.o
-
 obj-$(CONFIG_MTD_DOC2000)	+= doc2000.o
 obj-$(CONFIG_MTD_DOC2000)	+= doc2000.o
 obj-$(CONFIG_MTD_DOC2001)	+= doc2001.o
 obj-$(CONFIG_MTD_DOC2001)	+= doc2001.o
 obj-$(CONFIG_MTD_DOC2001PLUS)	+= doc2001plus.o
 obj-$(CONFIG_MTD_DOC2001PLUS)	+= doc2001plus.o

+ 20 - 7
drivers/mtd/devices/block2mtd.c

@@ -4,7 +4,7 @@
  * block2mtd.c - create an mtd from a block device
  * block2mtd.c - create an mtd from a block device
  *
  *
  * Copyright (C) 2001,2002	Simon Evans <spse@secret.org.uk>
  * Copyright (C) 2001,2002	Simon Evans <spse@secret.org.uk>
- * Copyright (C) 2004,2005	Jörn Engel <joern@wh.fh-wedel.de>
+ * Copyright (C) 2004-2006	Jörn Engel <joern@wh.fh-wedel.de>
  *
  *
  * Licence: GPL
  * Licence: GPL
  */
  */
@@ -331,7 +331,6 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
 	dev->mtd.writev = default_mtd_writev;
 	dev->mtd.writev = default_mtd_writev;
 	dev->mtd.sync = block2mtd_sync;
 	dev->mtd.sync = block2mtd_sync;
 	dev->mtd.read = block2mtd_read;
 	dev->mtd.read = block2mtd_read;
-	dev->mtd.readv = default_mtd_readv;
 	dev->mtd.priv = dev;
 	dev->mtd.priv = dev;
 	dev->mtd.owner = THIS_MODULE;
 	dev->mtd.owner = THIS_MODULE;
 
 
@@ -351,6 +350,12 @@ devinit_err:
 }
 }
 
 
 
 
+/* This function works similar to reguler strtoul.  In addition, it
+ * allows some suffixes for a more human-readable number format:
+ * ki, Ki, kiB, KiB	- multiply result with 1024
+ * Mi, MiB		- multiply result with 1024^2
+ * Gi, GiB		- multiply result with 1024^3
+ */
 static int ustrtoul(const char *cp, char **endp, unsigned int base)
 static int ustrtoul(const char *cp, char **endp, unsigned int base)
 {
 {
 	unsigned long result = simple_strtoul(cp, endp, base);
 	unsigned long result = simple_strtoul(cp, endp, base);
@@ -359,11 +364,16 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base)
 		result *= 1024;
 		result *= 1024;
 	case 'M':
 	case 'M':
 		result *= 1024;
 		result *= 1024;
+	case 'K':
 	case 'k':
 	case 'k':
 		result *= 1024;
 		result *= 1024;
 	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
 	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
-		if ((*endp)[1] == 'i')
-			(*endp) += 2;
+		if ((*endp)[1] == 'i') {
+			if ((*endp)[2] == 'B')
+				(*endp) += 3;
+			else
+				(*endp) += 2;
+		}
 	}
 	}
 	return result;
 	return result;
 }
 }
@@ -418,7 +428,8 @@ static inline void kill_final_newline(char *str)
 
 
 static int block2mtd_setup(const char *val, struct kernel_param *kp)
 static int block2mtd_setup(const char *val, struct kernel_param *kp)
 {
 {
-	char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */
+	char buf[80+12]; /* 80 for device, 12 for erase size */
+	char *str = buf;
 	char *token[2];
 	char *token[2];
 	char *name;
 	char *name;
 	size_t erase_size = PAGE_SIZE;
 	size_t erase_size = PAGE_SIZE;
@@ -430,7 +441,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp)
 	strcpy(str, val);
 	strcpy(str, val);
 	kill_final_newline(str);
 	kill_final_newline(str);
 
 
-	for (i=0; i<2; i++)
+	for (i = 0; i < 2; i++)
 		token[i] = strsep(&str, ",");
 		token[i] = strsep(&str, ",");
 
 
 	if (str)
 	if (str)
@@ -449,8 +460,10 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp)
 
 
 	if (token[1]) {
 	if (token[1]) {
 		ret = parse_num(&erase_size, token[1]);
 		ret = parse_num(&erase_size, token[1]);
-		if (ret)
+		if (ret) {
+			kfree(name);
 			parse_err("illegal erase size");
 			parse_err("illegal erase size");
+		}
 	}
 	}
 
 
 	add_device(name, erase_size);
 	add_device(name, erase_size);

+ 29 - 100
drivers/mtd/devices/doc2000.c

@@ -59,13 +59,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
 			size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
 			size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 			 size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
 			 size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
-static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
-			  unsigned long count, loff_t to, size_t *retlen,
-			  u_char *eccbuf, struct nand_oobinfo *oobsel);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			 size_t *retlen, const u_char *buf);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+			struct mtd_oob_ops *ops);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+			 struct mtd_oob_ops *ops);
 static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
 static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
 			 size_t *retlen, const u_char *buf);
 			 size_t *retlen, const u_char *buf);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
@@ -517,16 +514,9 @@ static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
 	return retval;
 	return retval;
 }
 }
 
 
-static const char im_name[] = "DoC2k_init";
-
-/* This routine is made available to other mtd code via
- * inter_module_register.  It must only be accessed through
- * inter_module_get which will bump the use count of this module.  The
- * addresses passed back in mtd are valid as long as the use count of
- * this module is non-zero, i.e. between inter_module_get and
- * inter_module_put.  Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
- */
-static void DoC2k_init(struct mtd_info *mtd)
+/* This routine is found from the docprobe code by symbol_get(),
+ * which will bump the use count of this module. */
+void DoC2k_init(struct mtd_info *mtd)
 {
 {
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *old = NULL;
 	struct DiskOnChip *old = NULL;
@@ -586,7 +576,7 @@ static void DoC2k_init(struct mtd_info *mtd)
 	mtd->ecctype = MTD_ECC_RS_DiskOnChip;
 	mtd->ecctype = MTD_ECC_RS_DiskOnChip;
 	mtd->size = 0;
 	mtd->size = 0;
 	mtd->erasesize = 0;
 	mtd->erasesize = 0;
-	mtd->oobblock = 512;
+	mtd->writesize = 512;
 	mtd->oobsize = 16;
 	mtd->oobsize = 16;
 	mtd->owner = THIS_MODULE;
 	mtd->owner = THIS_MODULE;
 	mtd->erase = doc_erase;
 	mtd->erase = doc_erase;
@@ -594,9 +584,6 @@ static void DoC2k_init(struct mtd_info *mtd)
 	mtd->unpoint = NULL;
 	mtd->unpoint = NULL;
 	mtd->read = doc_read;
 	mtd->read = doc_read;
 	mtd->write = doc_write;
 	mtd->write = doc_write;
-	mtd->read_ecc = doc_read_ecc;
-	mtd->write_ecc = doc_write_ecc;
-	mtd->writev_ecc = doc_writev_ecc;
 	mtd->read_oob = doc_read_oob;
 	mtd->read_oob = doc_read_oob;
 	mtd->write_oob = doc_write_oob;
 	mtd->write_oob = doc_write_oob;
 	mtd->sync = NULL;
 	mtd->sync = NULL;
@@ -623,6 +610,7 @@ static void DoC2k_init(struct mtd_info *mtd)
 		return;
 		return;
 	}
 	}
 }
 }
+EXPORT_SYMBOL_GPL(DoC2k_init);
 
 
 static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
 static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
 		    size_t * retlen, u_char * buf)
 		    size_t * retlen, u_char * buf)
@@ -971,72 +959,18 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 	return 0;
 	return 0;
 }
 }
 
 
-static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
-			  unsigned long count, loff_t to, size_t *retlen,
-			  u_char *eccbuf, struct nand_oobinfo *oobsel)
-{
-	static char static_buf[512];
-	static DEFINE_MUTEX(writev_buf_mutex);
-
-	size_t totretlen = 0;
-	size_t thisvecofs = 0;
-	int ret= 0;
-
-	mutex_lock(&writev_buf_mutex);
-
-	while(count) {
-		size_t thislen, thisretlen;
-		unsigned char *buf;
-
-		buf = vecs->iov_base + thisvecofs;
-		thislen = vecs->iov_len - thisvecofs;
-
-
-		if (thislen >= 512) {
-			thislen = thislen & ~(512-1);
-			thisvecofs += thislen;
-		} else {
-			/* Not enough to fill a page. Copy into buf */
-			memcpy(static_buf, buf, thislen);
-			buf = &static_buf[thislen];
-
-			while(count && thislen < 512) {
-				vecs++;
-				count--;
-				thisvecofs = min((512-thislen), vecs->iov_len);
-				memcpy(buf, vecs->iov_base, thisvecofs);
-				thislen += thisvecofs;
-				buf += thisvecofs;
-			}
-			buf = static_buf;
-		}
-		if (count && thisvecofs == vecs->iov_len) {
-			thisvecofs = 0;
-			vecs++;
-			count--;
-		}
-		ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel);
-
-		totretlen += thisretlen;
-
-		if (ret || thisretlen != thislen)
-			break;
-
-		to += thislen;
-	}
-
-	mutex_unlock(&writev_buf_mutex);
-	*retlen = totretlen;
-	return ret;
-}
-
-
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			size_t * retlen, u_char * buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+			struct mtd_oob_ops *ops)
 {
 {
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *this = mtd->priv;
 	int len256 = 0, ret;
 	int len256 = 0, ret;
 	struct Nand *mychip;
 	struct Nand *mychip;
+	uint8_t *buf = ops->oobbuf;
+	size_t len = ops->len;
+
+	BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+	ofs += ops->ooboffs;
 
 
 	mutex_lock(&this->lock);
 	mutex_lock(&this->lock);
 
 
@@ -1077,7 +1011,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 
 
 	DoC_ReadBuf(this, &buf[len256], len - len256);
 	DoC_ReadBuf(this, &buf[len256], len - len256);
 
 
-	*retlen = len;
+	ops->retlen = len;
 	/* Reading the full OOB data drops us off of the end of the page,
 	/* Reading the full OOB data drops us off of the end of the page,
          * causing the flash device to go into busy mode, so we need
          * causing the flash device to go into busy mode, so we need
          * to wait until ready 11.4.1 and Toshiba TC58256FT docs */
          * to wait until ready 11.4.1 and Toshiba TC58256FT docs */
@@ -1192,17 +1126,20 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
 
 
 }
 }
 
 
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- 			 size_t * retlen, const u_char * buf)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+			 struct mtd_oob_ops *ops)
 {
 {
- 	struct DiskOnChip *this = mtd->priv;
- 	int ret;
+	struct DiskOnChip *this = mtd->priv;
+	int ret;
 
 
- 	mutex_lock(&this->lock);
- 	ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf);
+	BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+	mutex_lock(&this->lock);
+	ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len,
+				   &ops->retlen, ops->oobbuf);
 
 
- 	mutex_unlock(&this->lock);
- 	return ret;
+	mutex_unlock(&this->lock);
+	return ret;
 }
 }
 
 
 static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
 static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
@@ -1277,12 +1214,6 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
  *
  *
  ****************************************************************************/
  ****************************************************************************/
 
 
-static int __init init_doc2000(void)
-{
-       inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
-       return 0;
-}
-
 static void __exit cleanup_doc2000(void)
 static void __exit cleanup_doc2000(void)
 {
 {
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
@@ -1298,11 +1229,9 @@ static void __exit cleanup_doc2000(void)
 		kfree(this->chips);
 		kfree(this->chips);
 		kfree(mtd);
 		kfree(mtd);
 	}
 	}
-	inter_module_unregister(im_name);
 }
 }
 
 
 module_exit(cleanup_doc2000);
 module_exit(cleanup_doc2000);
-module_init(init_doc2000);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");

+ 28 - 32
drivers/mtd/devices/doc2001.c

@@ -43,10 +43,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 			 size_t *retlen, const u_char *buf, u_char *eccbuf,
 			 size_t *retlen, const u_char *buf, u_char *eccbuf,
 			 struct nand_oobinfo *oobsel);
 			 struct nand_oobinfo *oobsel);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			 size_t *retlen, const u_char *buf);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+			struct mtd_oob_ops *ops);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+			 struct mtd_oob_ops *ops);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
 
 
 static struct mtd_info *docmillist = NULL;
 static struct mtd_info *docmillist = NULL;
@@ -324,16 +324,9 @@ static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
 	return retval;
 	return retval;
 }
 }
 
 
-static const char im_name[] = "DoCMil_init";
-
-/* This routine is made available to other mtd code via
- * inter_module_register.  It must only be accessed through
- * inter_module_get which will bump the use count of this module.  The
- * addresses passed back in mtd are valid as long as the use count of
- * this module is non-zero, i.e. between inter_module_get and
- * inter_module_put.  Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
- */
-static void DoCMil_init(struct mtd_info *mtd)
+/* This routine is found from the docprobe code by symbol_get(),
+ * which will bump the use count of this module. */
+void DoCMil_init(struct mtd_info *mtd)
 {
 {
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *old = NULL;
 	struct DiskOnChip *old = NULL;
@@ -368,7 +361,7 @@ static void DoCMil_init(struct mtd_info *mtd)
 	/* FIXME: erase size is not always 8KiB */
 	/* FIXME: erase size is not always 8KiB */
 	mtd->erasesize = 0x2000;
 	mtd->erasesize = 0x2000;
 
 
-	mtd->oobblock = 512;
+	mtd->writesize = 512;
 	mtd->oobsize = 16;
 	mtd->oobsize = 16;
 	mtd->owner = THIS_MODULE;
 	mtd->owner = THIS_MODULE;
 	mtd->erase = doc_erase;
 	mtd->erase = doc_erase;
@@ -376,8 +369,6 @@ static void DoCMil_init(struct mtd_info *mtd)
 	mtd->unpoint = NULL;
 	mtd->unpoint = NULL;
 	mtd->read = doc_read;
 	mtd->read = doc_read;
 	mtd->write = doc_write;
 	mtd->write = doc_write;
-	mtd->read_ecc = doc_read_ecc;
-	mtd->write_ecc = doc_write_ecc;
 	mtd->read_oob = doc_read_oob;
 	mtd->read_oob = doc_read_oob;
 	mtd->write_oob = doc_write_oob;
 	mtd->write_oob = doc_write_oob;
 	mtd->sync = NULL;
 	mtd->sync = NULL;
@@ -401,6 +392,7 @@ static void DoCMil_init(struct mtd_info *mtd)
 		return;
 		return;
 	}
 	}
 }
 }
+EXPORT_SYMBOL_GPL(DoCMil_init);
 
 
 static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
 static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
 		     size_t *retlen, u_char *buf)
 		     size_t *retlen, u_char *buf)
@@ -670,8 +662,8 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
 	return ret;
 	return ret;
 }
 }
 
 
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			size_t *retlen, u_char *buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+			struct mtd_oob_ops *ops)
 {
 {
 #ifndef USE_MEMCPY
 #ifndef USE_MEMCPY
 	int i;
 	int i;
@@ -680,6 +672,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *this = mtd->priv;
 	void __iomem *docptr = this->virtadr;
 	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+	uint8_t *buf = ops->oobbuf;
+	size_t len = ops->len;
+
+	BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+	ofs += ops->ooboffs;
 
 
 	/* Find the chip which is to be used and select it */
 	/* Find the chip which is to be used and select it */
 	if (this->curfloor != mychip->floor) {
 	if (this->curfloor != mychip->floor) {
@@ -716,13 +714,13 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 #endif
 #endif
 	buf[len - 1] = ReadDOC(docptr, LastDataRead);
 	buf[len - 1] = ReadDOC(docptr, LastDataRead);
 
 
-	*retlen = len;
+	ops->retlen = len;
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			 size_t *retlen, const u_char *buf)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+			 struct mtd_oob_ops *ops)
 {
 {
 #ifndef USE_MEMCPY
 #ifndef USE_MEMCPY
 	int i;
 	int i;
@@ -732,6 +730,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *this = mtd->priv;
 	void __iomem *docptr = this->virtadr;
 	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+	uint8_t *buf = ops->oobbuf;
+	size_t len = ops->len;
+
+	BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+	ofs += ops->ooboffs;
 
 
 	/* Find the chip which is to be used and select it */
 	/* Find the chip which is to be used and select it */
 	if (this->curfloor != mychip->floor) {
 	if (this->curfloor != mychip->floor) {
@@ -783,12 +787,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 	if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
 	if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
 		printk("Error programming oob data\n");
 		printk("Error programming oob data\n");
 		/* FIXME: implement Bad Block Replacement (in nftl.c ??) */
 		/* FIXME: implement Bad Block Replacement (in nftl.c ??) */
-		*retlen = 0;
+		ops->retlen = 0;
 		ret = -EIO;
 		ret = -EIO;
 	}
 	}
 	dummy = ReadDOC(docptr, LastDataRead);
 	dummy = ReadDOC(docptr, LastDataRead);
 
 
-	*retlen = len;
+	ops->retlen = len;
 
 
 	return ret;
 	return ret;
 }
 }
@@ -856,12 +860,6 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
  *
  *
  ****************************************************************************/
  ****************************************************************************/
 
 
-static int __init init_doc2001(void)
-{
-	inter_module_register(im_name, THIS_MODULE, &DoCMil_init);
-	return 0;
-}
-
 static void __exit cleanup_doc2001(void)
 static void __exit cleanup_doc2001(void)
 {
 {
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
@@ -877,11 +875,9 @@ static void __exit cleanup_doc2001(void)
 		kfree(this->chips);
 		kfree(this->chips);
 		kfree(mtd);
 		kfree(mtd);
 	}
 	}
-	inter_module_unregister(im_name);
 }
 }
 
 
 module_exit(cleanup_doc2001);
 module_exit(cleanup_doc2001);
-module_init(init_doc2001);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");

+ 28 - 32
drivers/mtd/devices/doc2001plus.c

@@ -47,10 +47,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 		size_t *retlen, const u_char *buf, u_char *eccbuf,
 		size_t *retlen, const u_char *buf, u_char *eccbuf,
 		struct nand_oobinfo *oobsel);
 		struct nand_oobinfo *oobsel);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-		size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-		size_t *retlen, const u_char *buf);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+			struct mtd_oob_ops *ops);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+			 struct mtd_oob_ops *ops);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
 
 
 static struct mtd_info *docmilpluslist = NULL;
 static struct mtd_info *docmilpluslist = NULL;
@@ -447,16 +447,9 @@ static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
 	return retval;
 	return retval;
 }
 }
 
 
-static const char im_name[] = "DoCMilPlus_init";
-
-/* This routine is made available to other mtd code via
- * inter_module_register.  It must only be accessed through
- * inter_module_get which will bump the use count of this module.  The
- * addresses passed back in mtd are valid as long as the use count of
- * this module is non-zero, i.e. between inter_module_get and
- * inter_module_put.  Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
- */
-static void DoCMilPlus_init(struct mtd_info *mtd)
+/* This routine is found from the docprobe code by symbol_get(),
+ * which will bump the use count of this module. */
+void DoCMilPlus_init(struct mtd_info *mtd)
 {
 {
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *old = NULL;
 	struct DiskOnChip *old = NULL;
@@ -490,7 +483,7 @@ static void DoCMilPlus_init(struct mtd_info *mtd)
 	mtd->size = 0;
 	mtd->size = 0;
 
 
 	mtd->erasesize = 0;
 	mtd->erasesize = 0;
-	mtd->oobblock = 512;
+	mtd->writesize = 512;
 	mtd->oobsize = 16;
 	mtd->oobsize = 16;
 	mtd->owner = THIS_MODULE;
 	mtd->owner = THIS_MODULE;
 	mtd->erase = doc_erase;
 	mtd->erase = doc_erase;
@@ -498,8 +491,6 @@ static void DoCMilPlus_init(struct mtd_info *mtd)
 	mtd->unpoint = NULL;
 	mtd->unpoint = NULL;
 	mtd->read = doc_read;
 	mtd->read = doc_read;
 	mtd->write = doc_write;
 	mtd->write = doc_write;
-	mtd->read_ecc = doc_read_ecc;
-	mtd->write_ecc = doc_write_ecc;
 	mtd->read_oob = doc_read_oob;
 	mtd->read_oob = doc_read_oob;
 	mtd->write_oob = doc_write_oob;
 	mtd->write_oob = doc_write_oob;
 	mtd->sync = NULL;
 	mtd->sync = NULL;
@@ -524,6 +515,7 @@ static void DoCMilPlus_init(struct mtd_info *mtd)
 		return;
 		return;
 	}
 	}
 }
 }
+EXPORT_SYMBOL_GPL(DoCMilPlus_init);
 
 
 #if 0
 #if 0
 static int doc_dumpblk(struct mtd_info *mtd, loff_t from)
 static int doc_dumpblk(struct mtd_info *mtd, loff_t from)
@@ -876,14 +868,20 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 	return ret;
 	return ret;
 }
 }
 
 
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			size_t *retlen, u_char *buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
+			struct mtd_oob_ops *ops)
 {
 {
 	loff_t fofs, base;
 	loff_t fofs, base;
 	struct DiskOnChip *this = mtd->priv;
 	struct DiskOnChip *this = mtd->priv;
 	void __iomem * docptr = this->virtadr;
 	void __iomem * docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 	size_t i, size, got, want;
 	size_t i, size, got, want;
+	uint8_t *buf = ops->oobbuf;
+	size_t len = ops->len;
+
+	BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+	ofs += ops->ooboffs;
 
 
 	DoC_CheckASIC(docptr);
 	DoC_CheckASIC(docptr);
 
 
@@ -949,12 +947,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 	/* Disable flash internally */
 	/* Disable flash internally */
 	WriteDOC(0, docptr, Mplus_FlashSelect);
 	WriteDOC(0, docptr, Mplus_FlashSelect);
 
 
-	*retlen = len;
+	ops->retlen = len;
 	return 0;
 	return 0;
 }
 }
 
 
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
-			 size_t *retlen, const u_char *buf)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
+			 struct mtd_oob_ops *ops)
 {
 {
 	volatile char dummy;
 	volatile char dummy;
 	loff_t fofs, base;
 	loff_t fofs, base;
@@ -963,6 +961,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 	size_t i, size, got, want;
 	size_t i, size, got, want;
 	int ret = 0;
 	int ret = 0;
+	uint8_t *buf = ops->oobbuf;
+	size_t len = ops->len;
+
+	BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+	ofs += ops->ooboffs;
 
 
 	DoC_CheckASIC(docptr);
 	DoC_CheckASIC(docptr);
 
 
@@ -1038,7 +1042,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 			printk("MTD: Error 0x%x programming oob at 0x%x\n",
 			printk("MTD: Error 0x%x programming oob at 0x%x\n",
 				dummy, (int)ofs);
 				dummy, (int)ofs);
 			/* FIXME: implement Bad Block Replacement */
 			/* FIXME: implement Bad Block Replacement */
-			*retlen = 0;
+			ops->retlen = 0;
 			ret = -EIO;
 			ret = -EIO;
 		}
 		}
 		dummy = ReadDOC(docptr, Mplus_LastDataRead);
 		dummy = ReadDOC(docptr, Mplus_LastDataRead);
@@ -1051,7 +1055,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 	/* Disable flash internally */
 	/* Disable flash internally */
 	WriteDOC(0, docptr, Mplus_FlashSelect);
 	WriteDOC(0, docptr, Mplus_FlashSelect);
 
 
-	*retlen = len;
+	ops->retlen = len;
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1122,12 +1126,6 @@ int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
  *
  *
  ****************************************************************************/
  ****************************************************************************/
 
 
-static int __init init_doc2001plus(void)
-{
-	inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init);
-	return 0;
-}
-
 static void __exit cleanup_doc2001plus(void)
 static void __exit cleanup_doc2001plus(void)
 {
 {
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
@@ -1143,11 +1141,9 @@ static void __exit cleanup_doc2001plus(void)
 		kfree(this->chips);
 		kfree(this->chips);
 		kfree(mtd);
 		kfree(mtd);
 	}
 	}
-	inter_module_unregister(im_name);
 }
 }
 
 
 module_exit(cleanup_doc2001plus);
 module_exit(cleanup_doc2001plus);
-module_init(init_doc2001plus);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al.");
 MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al.");

+ 10 - 16
drivers/mtd/devices/docprobe.c

@@ -231,6 +231,10 @@ static inline int __init doccheck(void __iomem *potential, unsigned long physadr
 
 
 static int docfound;
 static int docfound;
 
 
+extern void DoC2k_init(struct mtd_info *);
+extern void DoCMil_init(struct mtd_info *);
+extern void DoCMilPlus_init(struct mtd_info *);
+
 static void __init DoC_Probe(unsigned long physadr)
 static void __init DoC_Probe(unsigned long physadr)
 {
 {
 	void __iomem *docptr;
 	void __iomem *docptr;
@@ -239,8 +243,6 @@ static void __init DoC_Probe(unsigned long physadr)
 	int ChipID;
 	int ChipID;
 	char namebuf[15];
 	char namebuf[15];
 	char *name = namebuf;
 	char *name = namebuf;
-	char *im_funcname = NULL;
-	char *im_modname = NULL;
 	void (*initroutine)(struct mtd_info *) = NULL;
 	void (*initroutine)(struct mtd_info *) = NULL;
 
 
 	docptr = ioremap(physadr, DOC_IOREMAP_LEN);
 	docptr = ioremap(physadr, DOC_IOREMAP_LEN);
@@ -278,41 +280,33 @@ static void __init DoC_Probe(unsigned long physadr)
 		switch(ChipID) {
 		switch(ChipID) {
 		case DOC_ChipID_Doc2kTSOP:
 		case DOC_ChipID_Doc2kTSOP:
 			name="2000 TSOP";
 			name="2000 TSOP";
-			im_funcname = "DoC2k_init";
-			im_modname = "doc2000";
+			initroutine = symbol_request(DoC2k_init);
 			break;
 			break;
 
 
 		case DOC_ChipID_Doc2k:
 		case DOC_ChipID_Doc2k:
 			name="2000";
 			name="2000";
-			im_funcname = "DoC2k_init";
-			im_modname = "doc2000";
+			initroutine = symbol_request(DoC2k_init);
 			break;
 			break;
 
 
 		case DOC_ChipID_DocMil:
 		case DOC_ChipID_DocMil:
 			name="Millennium";
 			name="Millennium";
 #ifdef DOC_SINGLE_DRIVER
 #ifdef DOC_SINGLE_DRIVER
-			im_funcname = "DoC2k_init";
-			im_modname = "doc2000";
+			initroutine = symbol_request(DoC2k_init);
 #else
 #else
-			im_funcname = "DoCMil_init";
-			im_modname = "doc2001";
+			initroutine = symbol_request(DoCMil_init);
 #endif /* DOC_SINGLE_DRIVER */
 #endif /* DOC_SINGLE_DRIVER */
 			break;
 			break;
 
 
 		case DOC_ChipID_DocMilPlus16:
 		case DOC_ChipID_DocMilPlus16:
 		case DOC_ChipID_DocMilPlus32:
 		case DOC_ChipID_DocMilPlus32:
 			name="MillenniumPlus";
 			name="MillenniumPlus";
-			im_funcname = "DoCMilPlus_init";
-			im_modname = "doc2001plus";
+			initroutine = symbol_request(DoCMilPlus_init);
 			break;
 			break;
 		}
 		}
 
 
-		if (im_funcname)
-			initroutine = inter_module_get_request(im_funcname, im_modname);
-
 		if (initroutine) {
 		if (initroutine) {
 			(*initroutine)(mtd);
 			(*initroutine)(mtd);
-			inter_module_put(im_funcname);
+			symbol_put_addr(initroutine);
 			return;
 			return;
 		}
 		}
 		printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
 		printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);

+ 1 - 0
drivers/mtd/devices/lart.c

@@ -635,6 +635,7 @@ int __init lart_flash_init (void)
    printk ("%s: This looks like a LART board to me.\n",module_name);
    printk ("%s: This looks like a LART board to me.\n",module_name);
    mtd.name = module_name;
    mtd.name = module_name;
    mtd.type = MTD_NORFLASH;
    mtd.type = MTD_NORFLASH;
+   mtd.writesize = 1;
    mtd.flags = MTD_CAP_NORFLASH;
    mtd.flags = MTD_CAP_NORFLASH;
    mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
    mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
    mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
    mtd.erasesize = FLASH_BLOCKSIZE_MAIN;

+ 1 - 0
drivers/mtd/devices/m25p80.c

@@ -465,6 +465,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
 		flash->mtd.name = spi->dev.bus_id;
 		flash->mtd.name = spi->dev.bus_id;
 
 
 	flash->mtd.type = MTD_NORFLASH;
 	flash->mtd.type = MTD_NORFLASH;
+	flash->mtd.writesize = 1;
 	flash->mtd.flags = MTD_CAP_NORFLASH;
 	flash->mtd.flags = MTD_CAP_NORFLASH;
 	flash->mtd.size = info->sector_size * info->n_sectors;
 	flash->mtd.size = info->sector_size * info->n_sectors;
 	flash->mtd.erasesize = info->sector_size;
 	flash->mtd.erasesize = info->sector_size;

+ 1 - 1
drivers/mtd/devices/ms02-nv.c

@@ -219,7 +219,7 @@ static int __init ms02nv_init_one(ulong addr)
 	mp->uaddr = phys_to_virt(fixaddr);
 	mp->uaddr = phys_to_virt(fixaddr);
 
 
 	mtd->type = MTD_RAM;
 	mtd->type = MTD_RAM;
-	mtd->flags = MTD_CAP_RAM | MTD_XIP;
+	mtd->flags = MTD_CAP_RAM;
 	mtd->size = fixsize;
 	mtd->size = fixsize;
 	mtd->name = (char *)ms02nv_name;
 	mtd->name = (char *)ms02nv_name;
 	mtd->owner = THIS_MODULE;
 	mtd->owner = THIS_MODULE;

+ 1 - 0
drivers/mtd/devices/mtdram.c

@@ -106,6 +106,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
 	mtd->type = MTD_RAM;
 	mtd->type = MTD_RAM;
 	mtd->flags = MTD_CAP_RAM;
 	mtd->flags = MTD_CAP_RAM;
 	mtd->size = size;
 	mtd->size = size;
+	mtd->writesize = 1;
 	mtd->erasesize = MTDRAM_ERASE_SIZE;
 	mtd->erasesize = MTDRAM_ERASE_SIZE;
 	mtd->priv = mapped_address;
 	mtd->priv = mapped_address;
 
 

+ 10 - 6
drivers/mtd/devices/phram.c

@@ -1,8 +1,8 @@
 /**
 /**
  * $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $
  * $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $
  *
  *
- * Copyright (c) ????		Jochen Schäuble <psionic@psionic.de>
- * Copyright (c) 2003-2004	Jörn Engel <joern@wh.fh-wedel.de>
+ * Copyright (c) ????		Jochen Schäuble <psionic@psionic.de>
+ * Copyright (c) 2003-2004	Jörn Engel <joern@wh.fh-wedel.de>
  *
  *
  * Usage:
  * Usage:
  *
  *
@@ -142,7 +142,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
 
 
 	new->mtd.name = name;
 	new->mtd.name = name;
 	new->mtd.size = len;
 	new->mtd.size = len;
-	new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
+	new->mtd.flags = MTD_CAP_RAM;
         new->mtd.erase = phram_erase;
         new->mtd.erase = phram_erase;
 	new->mtd.point = phram_point;
 	new->mtd.point = phram_point;
 	new->mtd.unpoint = phram_unpoint;
 	new->mtd.unpoint = phram_unpoint;
@@ -266,12 +266,16 @@ static int phram_setup(const char *val, struct kernel_param *kp)
 		return 0;
 		return 0;
 
 
 	ret = parse_num32(&start, token[1]);
 	ret = parse_num32(&start, token[1]);
-	if (ret)
+	if (ret) {
+		kfree(name);
 		parse_err("illegal start address\n");
 		parse_err("illegal start address\n");
+	}
 
 
 	ret = parse_num32(&len, token[2]);
 	ret = parse_num32(&len, token[2]);
-	if (ret)
+	if (ret) {
+		kfree(name);
 		parse_err("illegal device length\n");
 		parse_err("illegal device length\n");
+	}
 
 
 	register_device(name, start, len);
 	register_device(name, start, len);
 
 
@@ -296,5 +300,5 @@ module_init(init_phram);
 module_exit(cleanup_phram);
 module_exit(cleanup_phram);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
+MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
 MODULE_DESCRIPTION("MTD driver for physical RAM");
 MODULE_DESCRIPTION("MTD driver for physical RAM");

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

@@ -200,8 +200,7 @@ static int register_device(char *name, unsigned long start, unsigned long length
 
 
 	(*curmtd)->mtdinfo->name = name;
 	(*curmtd)->mtdinfo->name = name;
 	(*curmtd)->mtdinfo->size = length;
 	(*curmtd)->mtdinfo->size = length;
-	(*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS |
-					MTD_WRITEB_WRITEABLE | MTD_VOLATILE | MTD_CAP_RAM;
+	(*curmtd)->mtdinfo->flags = MTD_CAP_RAM;
         (*curmtd)->mtdinfo->erase = slram_erase;
         (*curmtd)->mtdinfo->erase = slram_erase;
 	(*curmtd)->mtdinfo->point = slram_point;
 	(*curmtd)->mtdinfo->point = slram_point;
 	(*curmtd)->mtdinfo->unpoint = slram_unpoint;
 	(*curmtd)->mtdinfo->unpoint = slram_unpoint;

+ 132 - 60
drivers/mtd/inftlcore.c

@@ -36,6 +36,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nftl.h>
 #include <linux/mtd/nftl.h>
 #include <linux/mtd/inftl.h>
 #include <linux/mtd/inftl.h>
+#include <linux/mtd/nand.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <asm/errno.h>
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm/io.h>
@@ -79,14 +80,12 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 	inftl->mbd.devnum = -1;
 	inftl->mbd.devnum = -1;
 	inftl->mbd.blksize = 512;
 	inftl->mbd.blksize = 512;
 	inftl->mbd.tr = tr;
 	inftl->mbd.tr = tr;
-	memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
-	inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
 
 
-        if (INFTL_mount(inftl) < 0) {
+	if (INFTL_mount(inftl) < 0) {
 		printk(KERN_WARNING "INFTL: could not mount device\n");
 		printk(KERN_WARNING "INFTL: could not mount device\n");
 		kfree(inftl);
 		kfree(inftl);
 		return;
 		return;
-        }
+	}
 
 
 	/* OK, it's a new one. Set up all the data structures. */
 	/* OK, it's a new one. Set up all the data structures. */
 
 
@@ -151,6 +150,69 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
  * Actual INFTL access routines.
  * Actual INFTL access routines.
  */
  */
 
 
+/*
+ * Read oob data from flash
+ */
+int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+		   size_t *retlen, uint8_t *buf)
+{
+	struct mtd_oob_ops ops;
+	int res;
+
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = offs & (mtd->writesize - 1);
+	ops.ooblen = len;
+	ops.oobbuf = buf;
+	ops.datbuf = NULL;
+	ops.len = len;
+
+	res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+	*retlen = ops.retlen;
+	return res;
+}
+
+/*
+ * Write oob data to flash
+ */
+int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+		    size_t *retlen, uint8_t *buf)
+{
+	struct mtd_oob_ops ops;
+	int res;
+
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = offs & (mtd->writesize - 1);
+	ops.ooblen = len;
+	ops.oobbuf = buf;
+	ops.datbuf = NULL;
+	ops.len = len;
+
+	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+	*retlen = ops.retlen;
+	return res;
+}
+
+/*
+ * Write data and oob to flash
+ */
+static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
+		       size_t *retlen, uint8_t *buf, uint8_t *oob)
+{
+	struct mtd_oob_ops ops;
+	int res;
+
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = offs;
+	ops.ooblen = mtd->oobsize;
+	ops.oobbuf = oob;
+	ops.datbuf = buf;
+	ops.len = len;
+
+	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+	*retlen = ops.retlen;
+	return res;
+}
+
 /*
 /*
  * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
  * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
  *	This function is used when the give Virtual Unit Chain.
  *	This function is used when the give Virtual Unit Chain.
@@ -198,10 +260,11 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
 	u16 BlockMap[MAX_SECTORS_PER_UNIT];
 	u16 BlockMap[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
 	unsigned int thisEUN, prevEUN, status;
 	unsigned int thisEUN, prevEUN, status;
+	struct mtd_info *mtd = inftl->mbd.mtd;
 	int block, silly;
 	int block, silly;
 	unsigned int targetEUN;
 	unsigned int targetEUN;
 	struct inftl_oob oob;
 	struct inftl_oob oob;
-        size_t retlen;
+	size_t retlen;
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
 	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
 		"pending=%d)\n", inftl, thisVUC, pendingblock);
 		"pending=%d)\n", inftl, thisVUC, pendingblock);
@@ -221,18 +284,18 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
 	 * Scan to find the Erase Unit which holds the actual data for each
 	 * Scan to find the Erase Unit which holds the actual data for each
 	 * 512-byte block within the Chain.
 	 * 512-byte block within the Chain.
 	 */
 	 */
-        silly = MAX_LOOPS;
+	silly = MAX_LOOPS;
 	while (thisEUN < inftl->nb_blocks) {
 	while (thisEUN < inftl->nb_blocks) {
 		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
 		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
 			if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
 			if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
 				continue;
 				continue;
 
 
-			if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
-			     + (block * SECTORSIZE), 16 , &retlen,
-			     (char *)&oob) < 0)
+			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
+					   + (block * SECTORSIZE), 16, &retlen,
+					   (char *)&oob) < 0)
 				status = SECTOR_IGNORE;
 				status = SECTOR_IGNORE;
 			else
 			else
-                        	status = oob.b.Status | oob.b.Status1;
+				status = oob.b.Status | oob.b.Status1;
 
 
 			switch(status) {
 			switch(status) {
 			case SECTOR_FREE:
 			case SECTOR_FREE:
@@ -282,29 +345,31 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
 			continue;
 			continue;
 		}
 		}
 
 
-                /*
+		/*
 		 * Copy only in non free block (free blocks can only
 		 * Copy only in non free block (free blocks can only
                  * happen in case of media errors or deleted blocks).
                  * happen in case of media errors or deleted blocks).
 		 */
 		 */
-                if (BlockMap[block] == BLOCK_NIL)
-                        continue;
-
-                ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
-			BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
-			&retlen, movebuf);
-                if (ret < 0) {
-			ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
-				BlockMap[block]) + (block * SECTORSIZE),
-				SECTORSIZE, &retlen, movebuf);
+		if (BlockMap[block] == BLOCK_NIL)
+			continue;
+
+		ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
+				(block * SECTORSIZE), SECTORSIZE, &retlen,
+				movebuf);
+		if (ret < 0 && ret != -EUCLEAN) {
+			ret = mtd->read(mtd,
+					(inftl->EraseSize * BlockMap[block]) +
+					(block * SECTORSIZE), SECTORSIZE,
+					&retlen, movebuf);
 			if (ret != -EIO)
 			if (ret != -EIO)
-                        	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
-					"away on retry?\n");
-                }
-                memset(&oob, 0xff, sizeof(struct inftl_oob));
-                oob.b.Status = oob.b.Status1 = SECTOR_USED;
-                MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
-			(block * SECTORSIZE), SECTORSIZE, &retlen,
-			movebuf, (char *)&oob, &inftl->oobinfo);
+				DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
+				      "away on retry?\n");
+		}
+		memset(&oob, 0xff, sizeof(struct inftl_oob));
+		oob.b.Status = oob.b.Status1 = SECTOR_USED;
+
+		inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
+			    (block * SECTORSIZE), SECTORSIZE, &retlen,
+			    movebuf, (char *)&oob);
 	}
 	}
 
 
 	/*
 	/*
@@ -329,17 +394,17 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
 		if (thisEUN == targetEUN)
 		if (thisEUN == targetEUN)
 			break;
 			break;
 
 
-                if (INFTL_formatblock(inftl, thisEUN) < 0) {
+		if (INFTL_formatblock(inftl, thisEUN) < 0) {
 			/*
 			/*
 			 * Could not erase : mark block as reserved.
 			 * Could not erase : mark block as reserved.
 			 */
 			 */
 			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
 			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
-                } else {
+		} else {
 			/* Correctly erased : mark it as free */
 			/* Correctly erased : mark it as free */
 			inftl->PUtable[thisEUN] = BLOCK_FREE;
 			inftl->PUtable[thisEUN] = BLOCK_FREE;
 			inftl->PUtable[prevEUN] = BLOCK_NIL;
 			inftl->PUtable[prevEUN] = BLOCK_NIL;
 			inftl->numfreeEUNs++;
 			inftl->numfreeEUNs++;
-                }
+		}
 	}
 	}
 
 
 	return targetEUN;
 	return targetEUN;
@@ -415,6 +480,7 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
 	unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
 	unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
 	unsigned int thisEUN, writeEUN, prev_block, status;
 	unsigned int thisEUN, writeEUN, prev_block, status;
 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
+	struct mtd_info *mtd = inftl->mbd.mtd;
 	struct inftl_oob oob;
 	struct inftl_oob oob;
 	struct inftl_bci bci;
 	struct inftl_bci bci;
 	unsigned char anac, nacs, parity;
 	unsigned char anac, nacs, parity;
@@ -434,10 +500,10 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
 		silly = MAX_LOOPS;
 		silly = MAX_LOOPS;
 
 
 		while (thisEUN <= inftl->lastEUN) {
 		while (thisEUN <= inftl->lastEUN) {
-			MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
-				blockofs, 8, &retlen, (char *)&bci);
+			inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
+				       blockofs, 8, &retlen, (char *)&bci);
 
 
-                        status = bci.Status | bci.Status1;
+			status = bci.Status | bci.Status1;
 			DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
 			DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
 				"EUN %d is %x\n", block , writeEUN, status);
 				"EUN %d is %x\n", block , writeEUN, status);
 
 
@@ -522,8 +588,8 @@ hitused:
 		nacs = 0;
 		nacs = 0;
 		thisEUN = inftl->VUtable[thisVUC];
 		thisEUN = inftl->VUtable[thisVUC];
 		if (thisEUN != BLOCK_NIL) {
 		if (thisEUN != BLOCK_NIL) {
-			MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize
-				+ 8, 8, &retlen, (char *)&oob.u);
+			inftl_read_oob(mtd, thisEUN * inftl->EraseSize
+				       + 8, 8, &retlen, (char *)&oob.u);
 			anac = oob.u.a.ANAC + 1;
 			anac = oob.u.a.ANAC + 1;
 			nacs = oob.u.a.NACs + 1;
 			nacs = oob.u.a.NACs + 1;
 		}
 		}
@@ -544,8 +610,8 @@ hitused:
 		oob.u.a.parityPerField = parity;
 		oob.u.a.parityPerField = parity;
 		oob.u.a.discarded = 0xaa;
 		oob.u.a.discarded = 0xaa;
 
 
-		MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8,
-			&retlen, (char *)&oob.u);
+		inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
+				&retlen, (char *)&oob.u);
 
 
 		/* Also back up header... */
 		/* Also back up header... */
 		oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
 		oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
@@ -555,8 +621,8 @@ hitused:
 		oob.u.b.parityPerField = parity;
 		oob.u.b.parityPerField = parity;
 		oob.u.b.discarded = 0xaa;
 		oob.u.b.discarded = 0xaa;
 
 
-		MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize +
-			SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
+		inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
+				SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
 
 
 		inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
 		inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
 		inftl->VUtable[thisVUC] = writeEUN;
 		inftl->VUtable[thisVUC] = writeEUN;
@@ -576,6 +642,7 @@ hitused:
  */
  */
 static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
 static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
 {
 {
+	struct mtd_info *mtd = inftl->mbd.mtd;
 	unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
 	unsigned int thisEUN, status;
 	unsigned int thisEUN, status;
@@ -606,9 +673,9 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
 			if (BlockUsed[block] || BlockDeleted[block])
 			if (BlockUsed[block] || BlockDeleted[block])
 				continue;
 				continue;
 
 
-			if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
-			    + (block * SECTORSIZE), 8 , &retlen,
-			    (char *)&bci) < 0)
+			if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
+					   + (block * SECTORSIZE), 8 , &retlen,
+					  (char *)&bci) < 0)
 				status = SECTOR_IGNORE;
 				status = SECTOR_IGNORE;
 			else
 			else
 				status = bci.Status | bci.Status1;
 				status = bci.Status | bci.Status1;
@@ -670,12 +737,12 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
 		DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
 		DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
 		      thisEUN, thisVUC);
 		      thisEUN, thisVUC);
 
 
-                if (INFTL_formatblock(inftl, thisEUN) < 0) {
+		if (INFTL_formatblock(inftl, thisEUN) < 0) {
 			/*
 			/*
 			 * Could not erase : mark block as reserved.
 			 * Could not erase : mark block as reserved.
 			 */
 			 */
 			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
 			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
-                } else {
+		} else {
 			/* Correctly erased : mark it as free */
 			/* Correctly erased : mark it as free */
 			inftl->PUtable[thisEUN] = BLOCK_FREE;
 			inftl->PUtable[thisEUN] = BLOCK_FREE;
 			inftl->numfreeEUNs++;
 			inftl->numfreeEUNs++;
@@ -697,6 +764,7 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
 {
 {
 	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
 	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
+	struct mtd_info *mtd = inftl->mbd.mtd;
 	unsigned int status;
 	unsigned int status;
 	int silly = MAX_LOOPS;
 	int silly = MAX_LOOPS;
 	size_t retlen;
 	size_t retlen;
@@ -706,8 +774,8 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
 		"block=%d)\n", inftl, block);
 		"block=%d)\n", inftl, block);
 
 
 	while (thisEUN < inftl->nb_blocks) {
 	while (thisEUN < inftl->nb_blocks) {
-		if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
-		    blockofs, 8, &retlen, (char *)&bci) < 0)
+		if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
+				   blockofs, 8, &retlen, (char *)&bci) < 0)
 			status = SECTOR_IGNORE;
 			status = SECTOR_IGNORE;
 		else
 		else
 			status = bci.Status | bci.Status1;
 			status = bci.Status | bci.Status1;
@@ -741,10 +809,10 @@ foundit:
 	if (thisEUN != BLOCK_NIL) {
 	if (thisEUN != BLOCK_NIL) {
 		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
 		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
 
 
-		if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
+		if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
 			return -EIO;
 			return -EIO;
 		bci.Status = bci.Status1 = SECTOR_DELETED;
 		bci.Status = bci.Status1 = SECTOR_DELETED;
-		if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
+		if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
 			return -EIO;
 			return -EIO;
 		INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
 		INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
 	}
 	}
@@ -784,9 +852,10 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 
 
 		memset(&oob, 0xff, sizeof(struct inftl_oob));
 		memset(&oob, 0xff, sizeof(struct inftl_oob));
 		oob.b.Status = oob.b.Status1 = SECTOR_USED;
 		oob.b.Status = oob.b.Status1 = SECTOR_USED;
-		MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
-			blockofs, SECTORSIZE, &retlen, (char *)buffer,
-			(char *)&oob, &inftl->oobinfo);
+
+		inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
+			    blockofs, SECTORSIZE, &retlen, (char *)buffer,
+			    (char *)&oob);
 		/*
 		/*
 		 * need to write SECTOR_USED flags since they are not written
 		 * need to write SECTOR_USED flags since they are not written
 		 * in mtd_writeecc
 		 * in mtd_writeecc
@@ -804,17 +873,18 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 	struct INFTLrecord *inftl = (void *)mbd;
 	struct INFTLrecord *inftl = (void *)mbd;
 	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
 	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
 	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
-        unsigned int status;
+	struct mtd_info *mtd = inftl->mbd.mtd;
+	unsigned int status;
 	int silly = MAX_LOOPS;
 	int silly = MAX_LOOPS;
-        struct inftl_bci bci;
+	struct inftl_bci bci;
 	size_t retlen;
 	size_t retlen;
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
 	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
 		"buffer=%p)\n", inftl, block, buffer);
 		"buffer=%p)\n", inftl, block, buffer);
 
 
 	while (thisEUN < inftl->nb_blocks) {
 	while (thisEUN < inftl->nb_blocks) {
-		if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
-		     blockofs, 8, &retlen, (char *)&bci) < 0)
+		if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
+				  blockofs, 8, &retlen, (char *)&bci) < 0)
 			status = SECTOR_IGNORE;
 			status = SECTOR_IGNORE;
 		else
 		else
 			status = bci.Status | bci.Status1;
 			status = bci.Status | bci.Status1;
@@ -850,10 +920,12 @@ foundit:
 		/* The requested block is not on the media, return all 0x00 */
 		/* The requested block is not on the media, return all 0x00 */
 		memset(buffer, 0, SECTORSIZE);
 		memset(buffer, 0, SECTORSIZE);
 	} else {
 	} else {
-        	size_t retlen;
+		size_t retlen;
 		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
 		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
-		if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
-		    buffer))
+		int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer);
+
+		/* Handle corrected bit flips gracefully */
+		if (ret < 0 && ret != -EUCLEAN)
 			return -EIO;
 			return -EIO;
 	}
 	}
 	return 0;
 	return 0;

+ 35 - 25
drivers/mtd/inftlmount.c

@@ -43,6 +43,11 @@
 
 
 char inftlmountrev[]="$Revision: 1.18 $";
 char inftlmountrev[]="$Revision: 1.18 $";
 
 
+extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+			  size_t *retlen, uint8_t *buf);
+extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+			   size_t *retlen, uint8_t *buf);
+
 /*
 /*
  * find_boot_record: Find the INFTL Media Header and its Spare copy which
  * find_boot_record: Find the INFTL Media Header and its Spare copy which
  *	contains the various device information of the INFTL partition and
  *	contains the various device information of the INFTL partition and
@@ -57,6 +62,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
 	unsigned int i, block;
 	unsigned int i, block;
 	u8 buf[SECTORSIZE];
 	u8 buf[SECTORSIZE];
 	struct INFTLMediaHeader *mh = &inftl->MediaHdr;
 	struct INFTLMediaHeader *mh = &inftl->MediaHdr;
+	struct mtd_info *mtd = inftl->mbd.mtd;
 	struct INFTLPartition *ip;
 	struct INFTLPartition *ip;
 	size_t retlen;
 	size_t retlen;
 
 
@@ -80,8 +86,8 @@ static int find_boot_record(struct INFTLrecord *inftl)
 		 * Check for BNAND header first. Then whinge if it's found
 		 * Check for BNAND header first. Then whinge if it's found
 		 * but later checks fail.
 		 * but later checks fail.
 		 */
 		 */
-		ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
-		    SECTORSIZE, &retlen, buf);
+		ret = mtd->read(mtd, block * inftl->EraseSize,
+				SECTORSIZE, &retlen, buf);
 		/* We ignore ret in case the ECC of the MediaHeader is invalid
 		/* We ignore ret in case the ECC of the MediaHeader is invalid
 		   (which is apparently acceptable) */
 		   (which is apparently acceptable) */
 		if (retlen != SECTORSIZE) {
 		if (retlen != SECTORSIZE) {
@@ -106,8 +112,9 @@ static int find_boot_record(struct INFTLrecord *inftl)
 		}
 		}
 
 
 		/* To be safer with BIOS, also use erase mark as discriminant */
 		/* To be safer with BIOS, also use erase mark as discriminant */
-		if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize +
-		    SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) {
+		if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize +
+					  SECTORSIZE + 8, 8, &retlen,
+					  (char *)&h1) < 0)) {
 			printk(KERN_WARNING "INFTL: ANAND header found at "
 			printk(KERN_WARNING "INFTL: ANAND header found at "
 				"0x%x in mtd%d, but OOB data read failed "
 				"0x%x in mtd%d, but OOB data read failed "
 				"(err %d)\n", block * inftl->EraseSize,
 				"(err %d)\n", block * inftl->EraseSize,
@@ -123,8 +130,8 @@ static int find_boot_record(struct INFTLrecord *inftl)
 		memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
 		memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
 
 
 		/* Read the spare media header at offset 4096 */
 		/* Read the spare media header at offset 4096 */
-		MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096,
-		    SECTORSIZE, &retlen, buf);
+		mtd->read(mtd, block * inftl->EraseSize + 4096,
+			  SECTORSIZE, &retlen, buf);
 		if (retlen != SECTORSIZE) {
 		if (retlen != SECTORSIZE) {
 			printk(KERN_WARNING "INFTL: Unable to read spare "
 			printk(KERN_WARNING "INFTL: Unable to read spare "
 			       "Media Header\n");
 			       "Media Header\n");
@@ -233,7 +240,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
 				 */
 				 */
 				instr->addr = ip->Reserved0 * inftl->EraseSize;
 				instr->addr = ip->Reserved0 * inftl->EraseSize;
 				instr->len = inftl->EraseSize;
 				instr->len = inftl->EraseSize;
-				MTD_ERASE(inftl->mbd.mtd, instr);
+				mtd->erase(mtd, instr);
 			}
 			}
 			if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
 			if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
 				printk(KERN_WARNING "INFTL: Media Header "
 				printk(KERN_WARNING "INFTL: Media Header "
@@ -350,21 +357,21 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
 	int len, int check_oob)
 	int len, int check_oob)
 {
 {
 	u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];
 	u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];
+	struct mtd_info *mtd = inftl->mbd.mtd;
 	size_t retlen;
 	size_t retlen;
 	int i;
 	int i;
 
 
-	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=%p,"
-		"address=0x%x,len=%d,check_oob=%d)\n", inftl,
-		address, len, check_oob);
-
 	for (i = 0; i < len; i += SECTORSIZE) {
 	for (i = 0; i < len; i += SECTORSIZE) {
-		if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0)
+		if (mtd->read(mtd, address, SECTORSIZE, &retlen, buf))
 			return -1;
 			return -1;
 		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
 		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
 			return -1;
 			return -1;
 
 
 		if (check_oob) {
 		if (check_oob) {
-			if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0)
+			if(inftl_read_oob(mtd, address, mtd->oobsize,
+					  &retlen, &buf[SECTORSIZE]) < 0)
+				return -1;
+			if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
 				return -1;
 				return -1;
 		}
 		}
 		address += SECTORSIZE;
 		address += SECTORSIZE;
@@ -387,6 +394,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
 	size_t retlen;
 	size_t retlen;
 	struct inftl_unittail uci;
 	struct inftl_unittail uci;
 	struct erase_info *instr = &inftl->instr;
 	struct erase_info *instr = &inftl->instr;
+	struct mtd_info *mtd = inftl->mbd.mtd;
 	int physblock;
 	int physblock;
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p,"
 	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p,"
@@ -404,8 +412,9 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
 	/* Erase one physical eraseblock at a time, even though the NAND api
 	/* Erase one physical eraseblock at a time, even though the NAND api
 	   allows us to group them.  This way we if we have a failure, we can
 	   allows us to group them.  This way we if we have a failure, we can
 	   mark only the failed block in the bbt. */
 	   mark only the failed block in the bbt. */
-	for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) {
-		MTD_ERASE(inftl->mbd.mtd, instr);
+	for (physblock = 0; physblock < inftl->EraseSize;
+	     physblock += instr->len, instr->addr += instr->len) {
+		mtd->erase(inftl->mbd.mtd, instr);
 
 
 		if (instr->state == MTD_ERASE_FAILED) {
 		if (instr->state == MTD_ERASE_FAILED) {
 			printk(KERN_WARNING "INFTL: error while formatting block %d\n",
 			printk(KERN_WARNING "INFTL: error while formatting block %d\n",
@@ -414,10 +423,10 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
 		}
 		}
 
 
 		/*
 		/*
-	 	* Check the "freeness" of Erase Unit before updating metadata.
-	 	* FixMe: is this check really necessary? Since we have check the
-	 	*        return code after the erase operation.
-	 	*/
+		 * Check the "freeness" of Erase Unit before updating metadata.
+		 * FixMe: is this check really necessary? Since we have check
+		 * the return code after the erase operation.
+		 */
 		if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0)
 		if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0)
 			goto fail;
 			goto fail;
 	}
 	}
@@ -429,8 +438,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
 	uci.Reserved[2] = 0;
 	uci.Reserved[2] = 0;
 	uci.Reserved[3] = 0;
 	uci.Reserved[3] = 0;
 	instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
 	instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
-	if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr +
-	    8, 8, &retlen, (char *)&uci) < 0)
+	if (inftl_write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0)
 		goto fail;
 		goto fail;
 	return 0;
 	return 0;
 fail:
 fail:
@@ -549,6 +557,7 @@ void INFTL_dumpVUchains(struct INFTLrecord *s)
 
 
 int INFTL_mount(struct INFTLrecord *s)
 int INFTL_mount(struct INFTLrecord *s)
 {
 {
+	struct mtd_info *mtd = s->mbd.mtd;
 	unsigned int block, first_block, prev_block, last_block;
 	unsigned int block, first_block, prev_block, last_block;
 	unsigned int first_logical_block, logical_block, erase_mark;
 	unsigned int first_logical_block, logical_block, erase_mark;
 	int chain_length, do_format_chain;
 	int chain_length, do_format_chain;
@@ -607,10 +616,11 @@ int INFTL_mount(struct INFTLrecord *s)
 				break;
 				break;
 			}
 			}
 
 
-			if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8,
-			    8, &retlen, (char *)&h0) < 0 ||
-			    MTD_READOOB(s->mbd.mtd, block * s->EraseSize +
-			    2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) {
+			if (inftl_read_oob(mtd, block * s->EraseSize + 8,
+					   8, &retlen, (char *)&h0) < 0 ||
+			    inftl_read_oob(mtd, block * s->EraseSize +
+					   2 * SECTORSIZE + 8, 8, &retlen,
+					   (char *)&h1) < 0) {
 				/* Should never happen? */
 				/* Should never happen? */
 				do_format_chain++;
 				do_format_chain++;
 				break;
 				break;

+ 5 - 6
drivers/mtd/maps/Kconfig

@@ -37,7 +37,7 @@ config MTD_PHYSMAP_START
 config MTD_PHYSMAP_LEN
 config MTD_PHYSMAP_LEN
 	hex "Physical length of flash mapping"
 	hex "Physical length of flash mapping"
 	depends on MTD_PHYSMAP
 	depends on MTD_PHYSMAP
-	default "0x4000000"
+	default "0"
 	help
 	help
 	  This is the total length of the mapping of the flash chips on
 	  This is the total length of the mapping of the flash chips on
 	  your particular board. If there is space, or aliases, in the
 	  your particular board. If there is space, or aliases, in the
@@ -78,7 +78,7 @@ config MTD_PNC2000
 
 
 config MTD_SC520CDP
 config MTD_SC520CDP
 	tristate "CFI Flash device mapped on AMD SC520 CDP"
 	tristate "CFI Flash device mapped on AMD SC520 CDP"
-	depends on X86 && MTD_CFI
+	depends on X86 && MTD_CFI && MTD_CONCAT
 	help
 	help
 	  The SC520 CDP board has two banks of CFI-compliant chips and one
 	  The SC520 CDP board has two banks of CFI-compliant chips and one
 	  Dual-in-line JEDEC chip. This 'mapping' driver supports that
 	  Dual-in-line JEDEC chip. This 'mapping' driver supports that
@@ -109,7 +109,7 @@ config MTD_TS5500
 	  mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL.
 	  mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL.
 
 
 	  Note that jumper 3 ("Write Enable Drive A") must be set
 	  Note that jumper 3 ("Write Enable Drive A") must be set
-	  otherwise detection won't succeeed.
+	  otherwise detection won't succeed.
 
 
 config MTD_SBC_GXX
 config MTD_SBC_GXX
 	tristate "CFI Flash device mapped on Arcom SBC-GXx boards"
 	tristate "CFI Flash device mapped on Arcom SBC-GXx boards"
@@ -200,8 +200,8 @@ config MTD_TSUNAMI
 	  Support for the flash chip on Tsunami TIG bus.
 	  Support for the flash chip on Tsunami TIG bus.
 
 
 config MTD_LASAT
 config MTD_LASAT
-	tristate "Flash chips on LASAT board"
-	depends on LASAT
+	tristate "LASAT flash device"
+	depends on LASAT && MTD_CFI
 	help
 	help
 	  Support for the flash chips on the Lasat 100 and 200 boards.
 	  Support for the flash chips on the Lasat 100 and 200 boards.
 
 
@@ -561,7 +561,6 @@ config MTD_PCMCIA
 config MTD_PCMCIA_ANONYMOUS
 config MTD_PCMCIA_ANONYMOUS
 	bool "Use PCMCIA MTD drivers for anonymous PCMCIA cards"
 	bool "Use PCMCIA MTD drivers for anonymous PCMCIA cards"
 	depends on MTD_PCMCIA
 	depends on MTD_PCMCIA
-	default N
 	help
 	help
 	  If this option is enabled, PCMCIA cards which do not report
 	  If this option is enabled, PCMCIA cards which do not report
 	  anything about themselves are assumed to be MTD cards.
 	  anything about themselves are assumed to be MTD cards.

+ 2 - 2
drivers/mtd/maps/cfi_flagadm.c

@@ -1,5 +1,5 @@
 /*
 /*
- *  Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
+ *  Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
  *
  *
  *  $Id: cfi_flagadm.c,v 1.15 2005/11/07 11:14:26 gleixner Exp $
  *  $Id: cfi_flagadm.c,v 1.15 2005/11/07 11:14:26 gleixner Exp $
  *
  *
@@ -135,5 +135,5 @@ module_exit(cleanup_flagadm);
 
 
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>");
+MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>");
 MODULE_DESCRIPTION("MTD map driver for Flaga digital module");
 MODULE_DESCRIPTION("MTD map driver for Flaga digital module");

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

@@ -122,5 +122,5 @@ module_exit(cleanup_dbox2_flash);
 
 
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
+MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
 MODULE_DESCRIPTION("MTD map driver for D-Box 2 board");
 MODULE_DESCRIPTION("MTD map driver for D-Box 2 board");

+ 1 - 1
drivers/mtd/maps/mtx-1_flash.c

@@ -4,7 +4,7 @@
  * $Id: mtx-1_flash.c,v 1.2 2005/11/07 11:14:27 gleixner Exp $
  * $Id: mtx-1_flash.c,v 1.2 2005/11/07 11:14:27 gleixner Exp $
  *
  *
  * (C) 2005 Bruno Randolf <bruno.randolf@4g-systems.biz>
  * (C) 2005 Bruno Randolf <bruno.randolf@4g-systems.biz>
- * (C) 2005 Jörn Engel <joern@wohnheim.fh-wedel.de>
+ * (C) 2005 Jörn Engel <joern@wohnheim.fh-wedel.de>
  *
  *
  */
  */
 
 

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

@@ -20,6 +20,8 @@
 #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/kdev_t.h>
+#include <linux/root_dev.h>
 #include <asm/io.h>
 #include <asm/io.h>
 
 
 /****************************************************************************/
 /****************************************************************************/
@@ -188,7 +190,7 @@ int nettel_eraseconfig(void)
 		set_current_state(TASK_INTERRUPTIBLE);
 		set_current_state(TASK_INTERRUPTIBLE);
 		add_wait_queue(&wait_q, &wait);
 		add_wait_queue(&wait_q, &wait);
 
 
-		ret = MTD_ERASE(mtd, &nettel_erase);
+		ret = mtd->erase(mtd, &nettel_erase);
 		if (ret) {
 		if (ret) {
 			set_current_state(TASK_RUNNING);
 			set_current_state(TASK_RUNNING);
 			remove_wait_queue(&wait_q, &wait);
 			remove_wait_queue(&wait_q, &wait);

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

@@ -713,6 +713,7 @@ static void pcmciamtd_detach(struct pcmcia_device *link)
 
 
 	if(dev->mtd_info) {
 	if(dev->mtd_info) {
 		del_mtd_device(dev->mtd_info);
 		del_mtd_device(dev->mtd_info);
+		map_destroy(dev->mtd_info);
 		info("mtd%d: Removed", dev->mtd_info->index);
 		info("mtd%d: Removed", dev->mtd_info->index);
 	}
 	}
 
 

+ 186 - 69
drivers/mtd/maps/physmap.c

@@ -14,112 +14,229 @@
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
-#include <asm/io.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/map.h>
 #include <linux/config.h>
 #include <linux/config.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mtd/physmap.h>
+#include <asm/io.h>
 
 
-static struct mtd_info *mymtd;
-
-struct map_info physmap_map = {
-	.name = "phys_mapped_flash",
-	.phys = CONFIG_MTD_PHYSMAP_START,
-	.size = CONFIG_MTD_PHYSMAP_LEN,
-	.bankwidth = CONFIG_MTD_PHYSMAP_BANKWIDTH,
+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 int physmap_flash_remove(struct platform_device *dev)
+{
+	struct physmap_flash_info *info;
+	struct physmap_flash_data *physmap_data;
+
+	info = platform_get_drvdata(dev);
+	if (info == NULL)
+		return 0;
+	platform_set_drvdata(dev, NULL);
+
+	physmap_data = dev->dev.platform_data;
+
+	if (info->mtd != NULL) {
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
-static struct mtd_partition *mtd_parts;
-static int                   mtd_parts_nb;
+		if (info->nr_parts) {
+			del_mtd_partitions(info->mtd);
+			kfree(info->parts);
+		} else if (physmap_data->nr_parts) {
+			del_mtd_partitions(info->mtd);
+		} else {
+			del_mtd_device(info->mtd);
+		}
+#else
+		del_mtd_device(info->mtd);
+#endif
+		map_destroy(info->mtd);
+	}
 
 
-static int num_physmap_partitions;
-static struct mtd_partition *physmap_partitions;
+	if (info->map.virt != NULL)
+		iounmap((void *)info->map.virt);
 
 
-static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
+	if (info->res != NULL) {
+		release_resource(info->res);
+		kfree(info->res);
+	}
 
 
-void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
-{
-	physmap_partitions=parts;
-	num_physmap_partitions=num_parts;
+	return 0;
 }
 }
-#endif /* CONFIG_MTD_PARTITIONS */
 
 
-static int __init init_physmap(void)
+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
+
+static int physmap_flash_probe(struct platform_device *dev)
 {
 {
-	static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
-	const char **type;
+	struct physmap_flash_data *physmap_data;
+	struct physmap_flash_info *info;
+	const char **probe_type;
+	int err;
+
+	physmap_data = dev->dev.platform_data;
+	if (physmap_data == NULL)
+		return -ENODEV;
+
+       	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->start);
+
+	info = kmalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
+	if (info == NULL) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+	memset(info, 0, sizeof(*info));
 
 
-       	printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys);
-	physmap_map.virt = ioremap(physmap_map.phys, physmap_map.size);
+	platform_set_drvdata(dev, info);
 
 
-	if (!physmap_map.virt) {
-		printk("Failed to ioremap\n");
-		return -EIO;
+	info->res = request_mem_region(dev->resource->start,
+			dev->resource->end - dev->resource->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;
 	}
 	}
 
 
-	simple_map_init(&physmap_map);
+	info->map.name = dev->dev.bus_id;
+	info->map.phys = dev->resource->start;
+	info->map.size = dev->resource->end - dev->resource->start + 1;
+	info->map.bankwidth = physmap_data->width;
+	info->map.set_vpp = physmap_data->set_vpp;
+
+	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;
+	}
 
 
-	mymtd = NULL;
-	type = rom_probe_types;
-	for(; !mymtd && *type; type++) {
-		mymtd = do_map_probe(*type, &physmap_map);
+	simple_map_init(&info->map);
+
+	probe_type = rom_probe_types;
+	for (; info->mtd == NULL && *probe_type != NULL; probe_type++)
+		info->mtd = do_map_probe(*probe_type, &info->map);
+	if (info->mtd == NULL) {
+		dev_err(&dev->dev, "map_probe failed\n");
+		err = -ENXIO;
+		goto err_out;
 	}
 	}
-	if (mymtd) {
-		mymtd->owner = THIS_MODULE;
+	info->mtd->owner = THIS_MODULE;
 
 
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
-		mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes,
-						    &mtd_parts, 0);
+	err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0);
+	if (err > 0) {
+		add_mtd_partitions(info->mtd, info->parts, err);
+		return 0;
+	}
 
 
-		if (mtd_parts_nb > 0)
-		{
-			add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb);
-			return 0;
-		}
+	if (physmap_data->nr_parts) {
+		printk(KERN_NOTICE "Using physmap partition information\n");
+		add_mtd_partitions(info->mtd, physmap_data->parts,
+						physmap_data->nr_parts);
+		return 0;
+	}
+#endif
+
+	add_mtd_device(info->mtd);
+	return 0;
+
+err_out:
+	physmap_flash_remove(dev);
+	return err;
+}
+
+static struct platform_driver physmap_flash_driver = {
+	.probe		= physmap_flash_probe,
+	.remove		= physmap_flash_remove,
+	.driver		= {
+		.name	= "physmap-flash",
+	},
+};
 
 
-		if (num_physmap_partitions != 0)
-		{
-			printk(KERN_NOTICE
-			       "Using physmap partition definition\n");
-			add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions);
-			return 0;
-		}
 
 
+#ifdef CONFIG_MTD_PHYSMAP_LEN
+#if CONFIG_MTD_PHYSMAP_LEN != 0
+#warning using PHYSMAP compat code
+#define PHYSMAP_COMPAT
+#endif
 #endif
 #endif
-		add_mtd_device(mymtd);
 
 
-		return 0;
-	}
+#ifdef PHYSMAP_COMPAT
+static struct physmap_flash_data physmap_flash_data = {
+	.width		= CONFIG_MTD_PHYSMAP_BANKWIDTH,
+};
 
 
-	iounmap(physmap_map.virt);
-	return -ENXIO;
-}
+static struct resource physmap_flash_resource = {
+	.start		= CONFIG_MTD_PHYSMAP_START,
+	.end		= CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN,
+	.flags		= IORESOURCE_MEM,
+};
 
 
-static void __exit cleanup_physmap(void)
+static struct platform_device physmap_flash = {
+	.name		= "physmap-flash",
+	.id		= 0,
+	.dev		= {
+		.platform_data	= &physmap_flash_data,
+	},
+	.num_resources	= 1,
+	.resource	= &physmap_flash_resource,
+};
+
+void physmap_configure(unsigned long addr, unsigned long size,
+		int bankwidth, void (*set_vpp)(struct map_info *, int))
 {
 {
+	physmap_flash_resource.start = addr;
+	physmap_flash_resource.end = addr + size - 1;
+	physmap_flash_data.width = bankwidth;
+	physmap_flash_data.set_vpp = set_vpp;
+}
+
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
-	if (mtd_parts_nb) {
-		del_mtd_partitions(mymtd);
-		kfree(mtd_parts);
-	} else if (num_physmap_partitions) {
-		del_mtd_partitions(mymtd);
-	} else {
-		del_mtd_device(mymtd);
-	}
-#else
-	del_mtd_device(mymtd);
+void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
+{
+	physmap_flash_data.nr_parts = num_parts;
+	physmap_flash_data.parts = parts;
+}
+#endif
 #endif
 #endif
-	map_destroy(mymtd);
 
 
-	iounmap(physmap_map.virt);
-	physmap_map.virt = NULL;
+static int __init physmap_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&physmap_flash_driver);
+#ifdef PHYSMAP_COMPAT
+	if (err == 0)
+		platform_device_register(&physmap_flash);
+#endif
+
+	return err;
 }
 }
 
 
-module_init(init_physmap);
-module_exit(cleanup_physmap);
+static void __exit physmap_exit(void)
+{
+#ifdef PHYSMAP_COMPAT
+	platform_device_unregister(&physmap_flash);
+#endif
+	platform_driver_unregister(&physmap_flash_driver);
+}
 
 
+module_init(physmap_init);
+module_exit(physmap_exit);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");

+ 8 - 8
drivers/mtd/mtdblock.c

@@ -71,7 +71,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos,
 	set_current_state(TASK_INTERRUPTIBLE);
 	set_current_state(TASK_INTERRUPTIBLE);
 	add_wait_queue(&wait_q, &wait);
 	add_wait_queue(&wait_q, &wait);
 
 
-	ret = MTD_ERASE(mtd, &erase);
+	ret = mtd->erase(mtd, &erase);
 	if (ret) {
 	if (ret) {
 		set_current_state(TASK_RUNNING);
 		set_current_state(TASK_RUNNING);
 		remove_wait_queue(&wait_q, &wait);
 		remove_wait_queue(&wait_q, &wait);
@@ -88,7 +88,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos,
 	 * Next, writhe data to flash.
 	 * Next, writhe data to flash.
 	 */
 	 */
 
 
-	ret = MTD_WRITE (mtd, pos, len, &retlen, buf);
+	ret = mtd->write(mtd, pos, len, &retlen, buf);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 	if (retlen != len)
 	if (retlen != len)
@@ -138,7 +138,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
 		mtd->name, pos, len);
 		mtd->name, pos, len);
 
 
 	if (!sect_size)
 	if (!sect_size)
-		return MTD_WRITE (mtd, pos, len, &retlen, buf);
+		return mtd->write(mtd, pos, len, &retlen, buf);
 
 
 	while (len > 0) {
 	while (len > 0) {
 		unsigned long sect_start = (pos/sect_size)*sect_size;
 		unsigned long sect_start = (pos/sect_size)*sect_size;
@@ -170,7 +170,8 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
 			    mtdblk->cache_offset != sect_start) {
 			    mtdblk->cache_offset != sect_start) {
 				/* fill the cache with the current sector */
 				/* fill the cache with the current sector */
 				mtdblk->cache_state = STATE_EMPTY;
 				mtdblk->cache_state = STATE_EMPTY;
-				ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data);
+				ret = mtd->read(mtd, sect_start, sect_size,
+						&retlen, mtdblk->cache_data);
 				if (ret)
 				if (ret)
 					return ret;
 					return ret;
 				if (retlen != sect_size)
 				if (retlen != sect_size)
@@ -207,7 +208,7 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
 			mtd->name, pos, len);
 			mtd->name, pos, len);
 
 
 	if (!sect_size)
 	if (!sect_size)
-		return MTD_READ (mtd, pos, len, &retlen, buf);
+		return mtd->read(mtd, pos, len, &retlen, buf);
 
 
 	while (len > 0) {
 	while (len > 0) {
 		unsigned long sect_start = (pos/sect_size)*sect_size;
 		unsigned long sect_start = (pos/sect_size)*sect_size;
@@ -226,7 +227,7 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
 		    mtdblk->cache_offset == sect_start) {
 		    mtdblk->cache_offset == sect_start) {
 			memcpy (buf, mtdblk->cache_data + offset, size);
 			memcpy (buf, mtdblk->cache_data + offset, size);
 		} else {
 		} else {
-			ret = MTD_READ (mtd, pos, size, &retlen, buf);
+			ret = mtd->read(mtd, pos, size, &retlen, buf);
 			if (ret)
 			if (ret)
 				return ret;
 				return ret;
 			if (retlen != size)
 			if (retlen != size)
@@ -288,8 +289,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
 
 
 	mutex_init(&mtdblk->cache_mutex);
 	mutex_init(&mtdblk->cache_mutex);
 	mtdblk->cache_state = STATE_EMPTY;
 	mtdblk->cache_state = STATE_EMPTY;
-	if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM &&
-	    mtdblk->mtd->erasesize) {
+	if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) {
 		mtdblk->cache_size = mtdblk->mtd->erasesize;
 		mtdblk->cache_size = mtdblk->mtd->erasesize;
 		mtdblk->cache_data = NULL;
 		mtdblk->cache_data = NULL;
 	}
 	}

+ 1 - 3
drivers/mtd/mtdblock_ro.c

@@ -45,9 +45,7 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 	dev->blksize = 512;
 	dev->blksize = 512;
 	dev->size = mtd->size >> 9;
 	dev->size = mtd->size >> 9;
 	dev->tr = tr;
 	dev->tr = tr;
-	if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) !=
-	    (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE))
-		dev->readonly = 1;
+	dev->readonly = 1;
 
 
 	add_mtd_blktrans_dev(dev);
 	add_mtd_blktrans_dev(dev);
 }
 }

+ 233 - 94
drivers/mtd/mtdchar.c

@@ -49,24 +49,18 @@ static struct mtd_notifier notifier = {
 };
 };
 
 
 /*
 /*
- * We use file->private_data to store a pointer to the MTDdevice.
- * Since alighment is at least 32 bits, we have 2 bits free for OTP
- * modes as well.
+ * Data structure to hold the pointer to the mtd device as well
+ * as mode information ofr various use cases.
  */
  */
-
-#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L)
-
-#define MTD_MODE_OTP_FACT	1
-#define MTD_MODE_OTP_USER	2
-#define MTD_MODE(file)		((long)((file)->private_data) & 3)
-
-#define SET_MTD_MODE(file, mode) \
-	do { long __p = (long)((file)->private_data); \
-	     (file)->private_data = (void *)((__p & ~3L) | mode); } while (0)
+struct mtd_file_info {
+	struct mtd_info *mtd;
+	enum mtd_file_modes mode;
+};
 
 
 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
 {
 {
-	struct mtd_info *mtd = TO_MTD(file);
+	struct mtd_file_info *mfi = file->private_data;
+	struct mtd_info *mtd = mfi->mtd;
 
 
 	switch (orig) {
 	switch (orig) {
 	case 0:
 	case 0:
@@ -97,6 +91,7 @@ static int mtd_open(struct inode *inode, struct file *file)
 	int minor = iminor(inode);
 	int minor = iminor(inode);
 	int devnum = minor >> 1;
 	int devnum = minor >> 1;
 	struct mtd_info *mtd;
 	struct mtd_info *mtd;
+	struct mtd_file_info *mfi;
 
 
 	DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
 	DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
 
 
@@ -117,14 +112,20 @@ static int mtd_open(struct inode *inode, struct file *file)
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
-	file->private_data = mtd;
-
 	/* You can't open it RW if it's not a writeable device */
 	/* You can't open it RW if it's not a writeable device */
 	if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
 	if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
 		put_mtd_device(mtd);
 		put_mtd_device(mtd);
 		return -EACCES;
 		return -EACCES;
 	}
 	}
 
 
+	mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
+	if (!mfi) {
+		put_mtd_device(mtd);
+		return -ENOMEM;
+	}
+	mfi->mtd = mtd;
+	file->private_data = mfi;
+
 	return 0;
 	return 0;
 } /* mtd_open */
 } /* mtd_open */
 
 
@@ -132,16 +133,17 @@ static int mtd_open(struct inode *inode, struct file *file)
 
 
 static int mtd_close(struct inode *inode, struct file *file)
 static int mtd_close(struct inode *inode, struct file *file)
 {
 {
-	struct mtd_info *mtd;
+	struct mtd_file_info *mfi = file->private_data;
+	struct mtd_info *mtd = mfi->mtd;
 
 
 	DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 	DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 
 
-	mtd = TO_MTD(file);
-
 	if (mtd->sync)
 	if (mtd->sync)
 		mtd->sync(mtd);
 		mtd->sync(mtd);
 
 
 	put_mtd_device(mtd);
 	put_mtd_device(mtd);
+	file->private_data = NULL;
+	kfree(mfi);
 
 
 	return 0;
 	return 0;
 } /* mtd_close */
 } /* mtd_close */
@@ -153,7 +155,8 @@ static int mtd_close(struct inode *inode, struct file *file)
 
 
 static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
 static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
 {
 {
-	struct mtd_info *mtd = TO_MTD(file);
+	struct mtd_file_info *mfi = file->private_data;
+	struct mtd_info *mtd = mfi->mtd;
 	size_t retlen=0;
 	size_t retlen=0;
 	size_t total_retlen=0;
 	size_t total_retlen=0;
 	int ret=0;
 	int ret=0;
@@ -170,36 +173,58 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
 
 
 	/* FIXME: Use kiovec in 2.5 to lock down the user's buffers
 	/* FIXME: Use kiovec in 2.5 to lock down the user's buffers
 	   and pass them directly to the MTD functions */
 	   and pass them directly to the MTD functions */
+
+	if (count > MAX_KMALLOC_SIZE)
+		kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
+	else
+		kbuf=kmalloc(count, GFP_KERNEL);
+
+	if (!kbuf)
+		return -ENOMEM;
+
 	while (count) {
 	while (count) {
+
 		if (count > MAX_KMALLOC_SIZE)
 		if (count > MAX_KMALLOC_SIZE)
 			len = MAX_KMALLOC_SIZE;
 			len = MAX_KMALLOC_SIZE;
 		else
 		else
 			len = count;
 			len = count;
 
 
-		kbuf=kmalloc(len,GFP_KERNEL);
-		if (!kbuf)
-			return -ENOMEM;
-
-		switch (MTD_MODE(file)) {
-		case MTD_MODE_OTP_FACT:
+		switch (mfi->mode) {
+		case MTD_MODE_OTP_FACTORY:
 			ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
 			ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
 			break;
 			break;
 		case MTD_MODE_OTP_USER:
 		case MTD_MODE_OTP_USER:
 			ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
 			ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
 			break;
 			break;
+		case MTD_MODE_RAW:
+		{
+			struct mtd_oob_ops ops;
+
+			ops.mode = MTD_OOB_RAW;
+			ops.datbuf = kbuf;
+			ops.oobbuf = NULL;
+			ops.len = len;
+
+			ret = mtd->read_oob(mtd, *ppos, &ops);
+			retlen = ops.retlen;
+			break;
+		}
 		default:
 		default:
-			ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
+			ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);
 		}
 		}
 		/* Nand returns -EBADMSG on ecc errors, but it returns
 		/* Nand returns -EBADMSG on ecc errors, but it returns
 		 * the data. For our userspace tools it is important
 		 * the data. For our userspace tools it is important
 		 * to dump areas with ecc errors !
 		 * to dump areas with ecc errors !
+		 * For kernel internal usage it also might return -EUCLEAN
+		 * to signal the caller that a bitflip has occured and has
+		 * been corrected by the ECC algorithm.
 		 * Userspace software which accesses NAND this way
 		 * Userspace software which accesses NAND this way
 		 * must be aware of the fact that it deals with NAND
 		 * must be aware of the fact that it deals with NAND
 		 */
 		 */
-		if (!ret || (ret == -EBADMSG)) {
+		if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {
 			*ppos += retlen;
 			*ppos += retlen;
 			if (copy_to_user(buf, kbuf, retlen)) {
 			if (copy_to_user(buf, kbuf, retlen)) {
-			        kfree(kbuf);
+				kfree(kbuf);
 				return -EFAULT;
 				return -EFAULT;
 			}
 			}
 			else
 			else
@@ -215,15 +240,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
 			return ret;
 			return ret;
 		}
 		}
 
 
-		kfree(kbuf);
 	}
 	}
 
 
+	kfree(kbuf);
 	return total_retlen;
 	return total_retlen;
 } /* mtd_read */
 } /* mtd_read */
 
 
 static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
 static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
 {
 {
-	struct mtd_info *mtd = TO_MTD(file);
+	struct mtd_file_info *mfi = file->private_data;
+	struct mtd_info *mtd = mfi->mtd;
 	char *kbuf;
 	char *kbuf;
 	size_t retlen;
 	size_t retlen;
 	size_t total_retlen=0;
 	size_t total_retlen=0;
@@ -241,25 +267,28 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
 	if (!count)
 	if (!count)
 		return 0;
 		return 0;
 
 
+	if (count > MAX_KMALLOC_SIZE)
+		kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
+	else
+		kbuf=kmalloc(count, GFP_KERNEL);
+
+	if (!kbuf)
+		return -ENOMEM;
+
 	while (count) {
 	while (count) {
+
 		if (count > MAX_KMALLOC_SIZE)
 		if (count > MAX_KMALLOC_SIZE)
 			len = MAX_KMALLOC_SIZE;
 			len = MAX_KMALLOC_SIZE;
 		else
 		else
 			len = count;
 			len = count;
 
 
-		kbuf=kmalloc(len,GFP_KERNEL);
-		if (!kbuf) {
-			printk("kmalloc is null\n");
-			return -ENOMEM;
-		}
-
 		if (copy_from_user(kbuf, buf, len)) {
 		if (copy_from_user(kbuf, buf, len)) {
 			kfree(kbuf);
 			kfree(kbuf);
 			return -EFAULT;
 			return -EFAULT;
 		}
 		}
 
 
-		switch (MTD_MODE(file)) {
-		case MTD_MODE_OTP_FACT:
+		switch (mfi->mode) {
+		case MTD_MODE_OTP_FACTORY:
 			ret = -EROFS;
 			ret = -EROFS;
 			break;
 			break;
 		case MTD_MODE_OTP_USER:
 		case MTD_MODE_OTP_USER:
@@ -269,6 +298,21 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
 			}
 			}
 			ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
 			ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
 			break;
 			break;
+
+		case MTD_MODE_RAW:
+		{
+			struct mtd_oob_ops ops;
+
+			ops.mode = MTD_OOB_RAW;
+			ops.datbuf = kbuf;
+			ops.oobbuf = NULL;
+			ops.len = len;
+
+			ret = mtd->write_oob(mtd, *ppos, &ops);
+			retlen = ops.retlen;
+			break;
+		}
+
 		default:
 		default:
 			ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
 			ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
 		}
 		}
@@ -282,10 +326,9 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
 			kfree(kbuf);
 			kfree(kbuf);
 			return ret;
 			return ret;
 		}
 		}
-
-		kfree(kbuf);
 	}
 	}
 
 
+	kfree(kbuf);
 	return total_retlen;
 	return total_retlen;
 } /* mtd_write */
 } /* mtd_write */
 
 
@@ -299,13 +342,45 @@ static void mtdchar_erase_callback (struct erase_info *instr)
 	wake_up((wait_queue_head_t *)instr->priv);
 	wake_up((wait_queue_head_t *)instr->priv);
 }
 }
 
 
+#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
+static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
+{
+	struct mtd_info *mtd = mfi->mtd;
+	int ret = 0;
+
+	switch (mode) {
+	case MTD_OTP_FACTORY:
+		if (!mtd->read_fact_prot_reg)
+			ret = -EOPNOTSUPP;
+		else
+			mfi->mode = MTD_MODE_OTP_FACTORY;
+		break;
+	case MTD_OTP_USER:
+		if (!mtd->read_fact_prot_reg)
+			ret = -EOPNOTSUPP;
+		else
+			mfi->mode = MTD_MODE_OTP_USER;
+		break;
+	default:
+		ret = -EINVAL;
+	case MTD_OTP_OFF:
+		break;
+	}
+	return ret;
+}
+#else
+# define otp_select_filemode(f,m)	-EOPNOTSUPP
+#endif
+
 static int mtd_ioctl(struct inode *inode, struct file *file,
 static int mtd_ioctl(struct inode *inode, struct file *file,
 		     u_int cmd, u_long arg)
 		     u_int cmd, u_long arg)
 {
 {
-	struct mtd_info *mtd = TO_MTD(file);
+	struct mtd_file_info *mfi = file->private_data;
+	struct mtd_info *mtd = mfi->mtd;
 	void __user *argp = (void __user *)arg;
 	void __user *argp = (void __user *)arg;
 	int ret = 0;
 	int ret = 0;
 	u_long size;
 	u_long size;
+	struct mtd_info_user info;
 
 
 	DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
 	DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
 
 
@@ -341,7 +416,15 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 	}
 	}
 
 
 	case MEMGETINFO:
 	case MEMGETINFO:
-		if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user)))
+		info.type	= mtd->type;
+		info.flags	= mtd->flags;
+		info.size	= mtd->size;
+		info.erasesize	= mtd->erasesize;
+		info.writesize	= mtd->writesize;
+		info.oobsize	= mtd->oobsize;
+		info.ecctype	= mtd->ecctype;
+		info.eccsize	= mtd->eccsize;
+		if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
 			return -EFAULT;
 			return -EFAULT;
 		break;
 		break;
 
 
@@ -400,8 +483,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 	case MEMWRITEOOB:
 	case MEMWRITEOOB:
 	{
 	{
 		struct mtd_oob_buf buf;
 		struct mtd_oob_buf buf;
-		void *databuf;
-		ssize_t retlen;
+		struct mtd_oob_ops ops;
 
 
 		if(!(file->f_mode & 2))
 		if(!(file->f_mode & 2))
 			return -EPERM;
 			return -EPERM;
@@ -409,7 +491,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
 		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
 			return -EFAULT;
 			return -EFAULT;
 
 
-		if (buf.length > 0x4096)
+		if (buf.length > 4096)
 			return -EINVAL;
 			return -EINVAL;
 
 
 		if (!mtd->write_oob)
 		if (!mtd->write_oob)
@@ -421,21 +503,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 
 
-		databuf = kmalloc(buf.length, GFP_KERNEL);
-		if (!databuf)
+		ops.len = buf.length;
+		ops.ooblen = buf.length;
+		ops.ooboffs = buf.start & (mtd->oobsize - 1);
+		ops.datbuf = NULL;
+		ops.mode = MTD_OOB_PLACE;
+
+		if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
+			return -EINVAL;
+
+		ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
+		if (!ops.oobbuf)
 			return -ENOMEM;
 			return -ENOMEM;
 
 
-		if (copy_from_user(databuf, buf.ptr, buf.length)) {
-			kfree(databuf);
+		if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
+			kfree(ops.oobbuf);
 			return -EFAULT;
 			return -EFAULT;
 		}
 		}
 
 
-		ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
+		buf.start &= ~(mtd->oobsize - 1);
+		ret = mtd->write_oob(mtd, buf.start, &ops);
 
 
-		if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t)))
+		if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen,
+				 sizeof(uint32_t)))
 			ret = -EFAULT;
 			ret = -EFAULT;
 
 
-		kfree(databuf);
+		kfree(ops.oobbuf);
 		break;
 		break;
 
 
 	}
 	}
@@ -443,13 +536,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 	case MEMREADOOB:
 	case MEMREADOOB:
 	{
 	{
 		struct mtd_oob_buf buf;
 		struct mtd_oob_buf buf;
-		void *databuf;
-		ssize_t retlen;
+		struct mtd_oob_ops ops;
 
 
 		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
 		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
 			return -EFAULT;
 			return -EFAULT;
 
 
-		if (buf.length > 0x4096)
+		if (buf.length > 4096)
 			return -EINVAL;
 			return -EINVAL;
 
 
 		if (!mtd->read_oob)
 		if (!mtd->read_oob)
@@ -457,22 +549,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		else
 		else
 			ret = access_ok(VERIFY_WRITE, buf.ptr,
 			ret = access_ok(VERIFY_WRITE, buf.ptr,
 					buf.length) ? 0 : -EFAULT;
 					buf.length) ? 0 : -EFAULT;
-
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 
 
-		databuf = kmalloc(buf.length, GFP_KERNEL);
-		if (!databuf)
+		ops.len = buf.length;
+		ops.ooblen = buf.length;
+		ops.ooboffs = buf.start & (mtd->oobsize - 1);
+		ops.datbuf = NULL;
+		ops.mode = MTD_OOB_PLACE;
+
+		if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
+			return -EINVAL;
+
+		ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
+		if (!ops.oobbuf)
 			return -ENOMEM;
 			return -ENOMEM;
 
 
-		ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
+		buf.start &= ~(mtd->oobsize - 1);
+		ret = mtd->read_oob(mtd, buf.start, &ops);
 
 
-		if (put_user(retlen, (uint32_t __user *)argp))
+		if (put_user(ops.retlen, (uint32_t __user *)argp))
 			ret = -EFAULT;
 			ret = -EFAULT;
-		else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
+		else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf,
+						    ops.retlen))
 			ret = -EFAULT;
 			ret = -EFAULT;
 
 
-		kfree(databuf);
+		kfree(ops.oobbuf);
 		break;
 		break;
 	}
 	}
 
 
@@ -504,16 +606,22 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		break;
 		break;
 	}
 	}
 
 
-	case MEMSETOOBSEL:
-	{
-		if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo)))
-			return -EFAULT;
-		break;
-	}
-
+	/* Legacy interface */
 	case MEMGETOOBSEL:
 	case MEMGETOOBSEL:
 	{
 	{
-		if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo)))
+		struct nand_oobinfo oi;
+
+		if (!mtd->ecclayout)
+			return -EOPNOTSUPP;
+		if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
+			return -EINVAL;
+
+		oi.useecc = MTD_NANDECC_AUTOPLACE;
+		memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
+		memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
+		       sizeof(oi.oobfree));
+
+		if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
 			return -EFAULT;
 			return -EFAULT;
 		break;
 		break;
 	}
 	}
@@ -544,31 +652,17 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		break;
 		break;
 	}
 	}
 
 
-#ifdef CONFIG_MTD_OTP
+#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
 	case OTPSELECT:
 	case OTPSELECT:
 	{
 	{
 		int mode;
 		int mode;
 		if (copy_from_user(&mode, argp, sizeof(int)))
 		if (copy_from_user(&mode, argp, sizeof(int)))
 			return -EFAULT;
 			return -EFAULT;
-		SET_MTD_MODE(file, 0);
-		switch (mode) {
-		case MTD_OTP_FACTORY:
-			if (!mtd->read_fact_prot_reg)
-				ret = -EOPNOTSUPP;
-			else
-				SET_MTD_MODE(file, MTD_MODE_OTP_FACT);
-			break;
-		case MTD_OTP_USER:
-			if (!mtd->read_fact_prot_reg)
-				ret = -EOPNOTSUPP;
-			else
-				SET_MTD_MODE(file, MTD_MODE_OTP_USER);
-			break;
-		default:
-			ret = -EINVAL;
-		case MTD_OTP_OFF:
-			break;
-		}
+
+		mfi->mode = MTD_MODE_NORMAL;
+
+		ret = otp_select_filemode(mfi, mode);
+
 		file->f_pos = 0;
 		file->f_pos = 0;
 		break;
 		break;
 	}
 	}
@@ -580,8 +674,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 		if (!buf)
 		if (!buf)
 			return -ENOMEM;
 			return -ENOMEM;
 		ret = -EOPNOTSUPP;
 		ret = -EOPNOTSUPP;
-		switch (MTD_MODE(file)) {
-		case MTD_MODE_OTP_FACT:
+		switch (mfi->mode) {
+		case MTD_MODE_OTP_FACTORY:
 			if (mtd->get_fact_prot_info)
 			if (mtd->get_fact_prot_info)
 				ret = mtd->get_fact_prot_info(mtd, buf, 4096);
 				ret = mtd->get_fact_prot_info(mtd, buf, 4096);
 			break;
 			break;
@@ -589,6 +683,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 			if (mtd->get_user_prot_info)
 			if (mtd->get_user_prot_info)
 				ret = mtd->get_user_prot_info(mtd, buf, 4096);
 				ret = mtd->get_user_prot_info(mtd, buf, 4096);
 			break;
 			break;
+		default:
+			break;
 		}
 		}
 		if (ret >= 0) {
 		if (ret >= 0) {
 			if (cmd == OTPGETREGIONCOUNT) {
 			if (cmd == OTPGETREGIONCOUNT) {
@@ -607,7 +703,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 	{
 	{
 		struct otp_info info;
 		struct otp_info info;
 
 
-		if (MTD_MODE(file) != MTD_MODE_OTP_USER)
+		if (mfi->mode != MTD_MODE_OTP_USER)
 			return -EINVAL;
 			return -EINVAL;
 		if (copy_from_user(&info, argp, sizeof(info)))
 		if (copy_from_user(&info, argp, sizeof(info)))
 			return -EFAULT;
 			return -EFAULT;
@@ -618,6 +714,49 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 	}
 	}
 #endif
 #endif
 
 
+	case ECCGETLAYOUT:
+	{
+		if (!mtd->ecclayout)
+			return -EOPNOTSUPP;
+
+		if (copy_to_user(argp, &mtd->ecclayout,
+				 sizeof(struct nand_ecclayout)))
+			return -EFAULT;
+		break;
+	}
+
+	case ECCGETSTATS:
+	{
+		if (copy_to_user(argp, &mtd->ecc_stats,
+				 sizeof(struct mtd_ecc_stats)))
+			return -EFAULT;
+		break;
+	}
+
+	case MTDFILEMODE:
+	{
+		mfi->mode = 0;
+
+		switch(arg) {
+		case MTD_MODE_OTP_FACTORY:
+		case MTD_MODE_OTP_USER:
+			ret = otp_select_filemode(mfi, arg);
+			break;
+
+		case MTD_MODE_RAW:
+			if (!mtd->read_oob || !mtd->write_oob)
+				return -EOPNOTSUPP;
+			mfi->mode = arg;
+
+		case MTD_MODE_NORMAL:
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		file->f_pos = 0;
+		break;
+	}
+
 	default:
 	default:
 		ret = -ENOTTY;
 		ret = -ENOTTY;
 	}
 	}

+ 188 - 147
drivers/mtd/mtdconcat.c

@@ -19,6 +19,8 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/concat.h>
 #include <linux/mtd/concat.h>
 
 
+#include <asm/div64.h>
+
 /*
 /*
  * Our storage structure:
  * Our storage structure:
  * Subdev points to an array of pointers to struct mtd_info objects
  * Subdev points to an array of pointers to struct mtd_info objects
@@ -54,7 +56,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
 	    size_t * retlen, u_char * buf)
 	    size_t * retlen, u_char * buf)
 {
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	struct mtd_concat *concat = CONCAT(mtd);
-	int err = -EINVAL;
+	int ret = 0, err;
 	int i;
 	int i;
 
 
 	*retlen = 0;
 	*retlen = 0;
@@ -78,19 +80,29 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
 
 
 		err = subdev->read(subdev, from, size, &retsize, buf);
 		err = subdev->read(subdev, from, size, &retsize, buf);
 
 
-		if (err)
-			break;
+		/* Save information about bitflips! */
+		if (unlikely(err)) {
+			if (err == -EBADMSG) {
+				mtd->ecc_stats.failed++;
+				ret = err;
+			} else if (err == -EUCLEAN) {
+				mtd->ecc_stats.corrected++;
+				/* Do not overwrite -EBADMSG !! */
+				if (!ret)
+					ret = err;
+			} else
+				return err;
+		}
 
 
 		*retlen += retsize;
 		*retlen += retsize;
 		len -= size;
 		len -= size;
 		if (len == 0)
 		if (len == 0)
-			break;
+			return ret;
 
 
-		err = -EINVAL;
 		buf += size;
 		buf += size;
 		from = 0;
 		from = 0;
 	}
 	}
-	return err;
+	return -EINVAL;
 }
 }
 
 
 static int
 static int
@@ -141,211 +153,185 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
 }
 }
 
 
 static int
 static int
-concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
-		size_t * retlen, u_char * buf, u_char * eccbuf,
-		struct nand_oobinfo *oobsel)
+concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
+		unsigned long count, loff_t to, size_t * retlen)
 {
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	struct mtd_concat *concat = CONCAT(mtd);
-	int err = -EINVAL;
+	struct kvec *vecs_copy;
+	unsigned long entry_low, entry_high;
+	size_t total_len = 0;
 	int i;
 	int i;
+	int err = -EINVAL;
 
 
-	*retlen = 0;
-
-	for (i = 0; i < concat->num_subdev; i++) {
-		struct mtd_info *subdev = concat->subdev[i];
-		size_t size, retsize;
-
-		if (from >= subdev->size) {
-			/* Not destined for this subdev */
-			size = 0;
-			from -= subdev->size;
-			continue;
-		}
-
-		if (from + len > subdev->size)
-			/* First part goes into this subdev */
-			size = subdev->size - from;
-		else
-			/* Entire transaction goes into this subdev */
-			size = len;
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
 
 
-		if (subdev->read_ecc)
-			err = subdev->read_ecc(subdev, from, size,
-					       &retsize, buf, eccbuf, oobsel);
-		else
-			err = -EINVAL;
+	*retlen = 0;
 
 
-		if (err)
-			break;
+	/* Calculate total length of data */
+	for (i = 0; i < count; i++)
+		total_len += vecs[i].iov_len;
 
 
-		*retlen += retsize;
-		len -= size;
-		if (len == 0)
-			break;
+	/* Do not allow write past end of device */
+	if ((to + total_len) > mtd->size)
+		return -EINVAL;
 
 
-		err = -EINVAL;
-		buf += size;
-		if (eccbuf) {
-			eccbuf += subdev->oobsize;
-			/* in nand.c at least, eccbufs are
-			   tagged with 2 (int)eccstatus'; we
-			   must account for these */
-			eccbuf += 2 * (sizeof (int));
-		}
-		from = 0;
+	/* Check alignment */
+	if (mtd->writesize > 1) {
+		loff_t __to = to;
+		if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
+			return -EINVAL;
 	}
 	}
-	return err;
-}
 
 
-static int
-concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
-		 size_t * retlen, const u_char * buf, u_char * eccbuf,
-		 struct nand_oobinfo *oobsel)
-{
-	struct mtd_concat *concat = CONCAT(mtd);
-	int err = -EINVAL;
-	int i;
-
-	if (!(mtd->flags & MTD_WRITEABLE))
-		return -EROFS;
-
-	*retlen = 0;
+	/* make a copy of vecs */
+	vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL);
+	if (!vecs_copy)
+		return -ENOMEM;
+	memcpy(vecs_copy, vecs, sizeof(struct kvec) * count);
 
 
+	entry_low = 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];
-		size_t size, retsize;
+		size_t size, wsize, retsize, old_iov_len;
 
 
 		if (to >= subdev->size) {
 		if (to >= subdev->size) {
-			size = 0;
 			to -= subdev->size;
 			to -= subdev->size;
 			continue;
 			continue;
 		}
 		}
-		if (to + len > subdev->size)
-			size = subdev->size - to;
-		else
-			size = len;
+
+		size = min(total_len, (size_t)(subdev->size - to));
+		wsize = size; /* store for future use */
+
+		entry_high = entry_low;
+		while (entry_high < count) {
+			if (size <= vecs_copy[entry_high].iov_len)
+				break;
+			size -= vecs_copy[entry_high++].iov_len;
+		}
+
+		old_iov_len = vecs_copy[entry_high].iov_len;
+		vecs_copy[entry_high].iov_len = size;
 
 
 		if (!(subdev->flags & MTD_WRITEABLE))
 		if (!(subdev->flags & MTD_WRITEABLE))
 			err = -EROFS;
 			err = -EROFS;
-		else if (subdev->write_ecc)
-			err = subdev->write_ecc(subdev, to, size,
-						&retsize, buf, eccbuf, oobsel);
 		else
 		else
-			err = -EINVAL;
+			err = subdev->writev(subdev, &vecs_copy[entry_low],
+				entry_high - entry_low + 1, to, &retsize);
+
+		vecs_copy[entry_high].iov_len = old_iov_len - size;
+		vecs_copy[entry_high].iov_base += size;
+
+		entry_low = entry_high;
 
 
 		if (err)
 		if (err)
 			break;
 			break;
 
 
 		*retlen += retsize;
 		*retlen += retsize;
-		len -= size;
-		if (len == 0)
+		total_len -= wsize;
+
+		if (total_len == 0)
 			break;
 			break;
 
 
 		err = -EINVAL;
 		err = -EINVAL;
-		buf += size;
-		if (eccbuf)
-			eccbuf += subdev->oobsize;
 		to = 0;
 		to = 0;
 	}
 	}
+
+	kfree(vecs_copy);
 	return err;
 	return err;
 }
 }
 
 
 static int
 static int
-concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-		size_t * retlen, u_char * buf)
+concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
 {
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	struct mtd_concat *concat = CONCAT(mtd);
-	int err = -EINVAL;
-	int i;
+	struct mtd_oob_ops devops = *ops;
+	int i, err, ret = 0;
 
 
-	*retlen = 0;
+	ops->retlen = 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];
-		size_t size, retsize;
 
 
 		if (from >= subdev->size) {
 		if (from >= subdev->size) {
-			/* Not destined for this subdev */
-			size = 0;
 			from -= subdev->size;
 			from -= subdev->size;
 			continue;
 			continue;
 		}
 		}
-		if (from + len > subdev->size)
-			/* First part goes into this subdev */
-			size = subdev->size - from;
-		else
-			/* Entire transaction goes into this subdev */
-			size = len;
 
 
-		if (subdev->read_oob)
-			err = subdev->read_oob(subdev, from, size,
-					       &retsize, buf);
-		else
-			err = -EINVAL;
+		/* partial read ? */
+		if (from + devops.len > subdev->size)
+			devops.len = subdev->size - from;
+
+		err = subdev->read_oob(subdev, from, &devops);
+		ops->retlen += devops.retlen;
+
+		/* Save information about bitflips! */
+		if (unlikely(err)) {
+			if (err == -EBADMSG) {
+				mtd->ecc_stats.failed++;
+				ret = err;
+			} else if (err == -EUCLEAN) {
+				mtd->ecc_stats.corrected++;
+				/* Do not overwrite -EBADMSG !! */
+				if (!ret)
+					ret = err;
+			} else
+				return err;
+		}
 
 
-		if (err)
-			break;
+		devops.len = ops->len - ops->retlen;
+		if (!devops.len)
+			return ret;
 
 
-		*retlen += retsize;
-		len -= size;
-		if (len == 0)
-			break;
+		if (devops.datbuf)
+			devops.datbuf += devops.retlen;
+		if (devops.oobbuf)
+			devops.oobbuf += devops.ooblen;
 
 
-		err = -EINVAL;
-		buf += size;
 		from = 0;
 		from = 0;
 	}
 	}
-	return err;
+	return -EINVAL;
 }
 }
 
 
 static int
 static int
-concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
-		 size_t * retlen, const u_char * buf)
+concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
 {
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	struct mtd_concat *concat = CONCAT(mtd);
-	int err = -EINVAL;
-	int i;
+	struct mtd_oob_ops devops = *ops;
+	int i, err;
 
 
 	if (!(mtd->flags & MTD_WRITEABLE))
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
 		return -EROFS;
 
 
-	*retlen = 0;
+	ops->retlen = 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];
-		size_t size, retsize;
 
 
 		if (to >= subdev->size) {
 		if (to >= subdev->size) {
-			size = 0;
 			to -= subdev->size;
 			to -= subdev->size;
 			continue;
 			continue;
 		}
 		}
-		if (to + len > subdev->size)
-			size = subdev->size - to;
-		else
-			size = len;
 
 
-		if (!(subdev->flags & MTD_WRITEABLE))
-			err = -EROFS;
-		else if (subdev->write_oob)
-			err = subdev->write_oob(subdev, to, size, &retsize,
-						buf);
-		else
-			err = -EINVAL;
+		/* partial write ? */
+		if (to + devops.len > subdev->size)
+			devops.len = subdev->size - to;
 
 
+		err = subdev->write_oob(subdev, to, &devops);
+		ops->retlen += devops.retlen;
 		if (err)
 		if (err)
-			break;
+			return err;
 
 
-		*retlen += retsize;
-		len -= size;
-		if (len == 0)
-			break;
+		devops.len = ops->len - ops->retlen;
+		if (!devops.len)
+			return 0;
 
 
-		err = -EINVAL;
-		buf += size;
+		if (devops.datbuf)
+			devops.datbuf += devops.retlen;
+		if (devops.oobbuf)
+			devops.oobbuf += devops.ooblen;
 		to = 0;
 		to = 0;
 	}
 	}
-	return err;
+	return -EINVAL;
 }
 }
 
 
 static void concat_erase_callback(struct erase_info *instr)
 static void concat_erase_callback(struct erase_info *instr)
@@ -636,6 +622,60 @@ static void concat_resume(struct mtd_info *mtd)
 	}
 	}
 }
 }
 
 
+static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_concat *concat = CONCAT(mtd);
+	int i, res = 0;
+
+	if (!concat->subdev[0]->block_isbad)
+		return res;
+
+	if (ofs > mtd->size)
+		return -EINVAL;
+
+	for (i = 0; i < concat->num_subdev; i++) {
+		struct mtd_info *subdev = concat->subdev[i];
+
+		if (ofs >= subdev->size) {
+			ofs -= subdev->size;
+			continue;
+		}
+
+		res = subdev->block_isbad(subdev, ofs);
+		break;
+	}
+
+	return res;
+}
+
+static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_concat *concat = CONCAT(mtd);
+	int i, err = -EINVAL;
+
+	if (!concat->subdev[0]->block_markbad)
+		return 0;
+
+	if (ofs > mtd->size)
+		return -EINVAL;
+
+	for (i = 0; i < concat->num_subdev; i++) {
+		struct mtd_info *subdev = concat->subdev[i];
+
+		if (ofs >= subdev->size) {
+			ofs -= subdev->size;
+			continue;
+		}
+
+		err = subdev->block_markbad(subdev, ofs);
+		if (!err)
+			mtd->ecc_stats.badblocks++;
+		break;
+	}
+
+	return err;
+}
+
 /*
 /*
  * This function constructs a virtual MTD device by concatenating
  * This function constructs a virtual MTD device by concatenating
  * num_devs MTD devices. A pointer to the new device object is
  * num_devs MTD devices. A pointer to the new device object is
@@ -677,18 +717,22 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to c
 	concat->mtd.flags = subdev[0]->flags;
 	concat->mtd.flags = subdev[0]->flags;
 	concat->mtd.size = subdev[0]->size;
 	concat->mtd.size = subdev[0]->size;
 	concat->mtd.erasesize = subdev[0]->erasesize;
 	concat->mtd.erasesize = subdev[0]->erasesize;
-	concat->mtd.oobblock = subdev[0]->oobblock;
+	concat->mtd.writesize = subdev[0]->writesize;
 	concat->mtd.oobsize = subdev[0]->oobsize;
 	concat->mtd.oobsize = subdev[0]->oobsize;
 	concat->mtd.ecctype = subdev[0]->ecctype;
 	concat->mtd.ecctype = subdev[0]->ecctype;
 	concat->mtd.eccsize = subdev[0]->eccsize;
 	concat->mtd.eccsize = subdev[0]->eccsize;
-	if (subdev[0]->read_ecc)
-		concat->mtd.read_ecc = concat_read_ecc;
-	if (subdev[0]->write_ecc)
-		concat->mtd.write_ecc = concat_write_ecc;
+	if (subdev[0]->writev)
+		concat->mtd.writev = concat_writev;
 	if (subdev[0]->read_oob)
 	if (subdev[0]->read_oob)
 		concat->mtd.read_oob = concat_read_oob;
 		concat->mtd.read_oob = concat_read_oob;
 	if (subdev[0]->write_oob)
 	if (subdev[0]->write_oob)
 		concat->mtd.write_oob = concat_write_oob;
 		concat->mtd.write_oob = concat_write_oob;
+	if (subdev[0]->block_isbad)
+		concat->mtd.block_isbad = concat_block_isbad;
+	if (subdev[0]->block_markbad)
+		concat->mtd.block_markbad = concat_block_markbad;
+
+	concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
 
 
 	concat->subdev[0] = subdev[0];
 	concat->subdev[0] = subdev[0];
 
 
@@ -717,12 +761,12 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to c
 				    subdev[i]->flags & MTD_WRITEABLE;
 				    subdev[i]->flags & MTD_WRITEABLE;
 		}
 		}
 		concat->mtd.size += subdev[i]->size;
 		concat->mtd.size += subdev[i]->size;
-		if (concat->mtd.oobblock   !=  subdev[i]->oobblock ||
+		concat->mtd.ecc_stats.badblocks +=
+			subdev[i]->ecc_stats.badblocks;
+		if (concat->mtd.writesize   !=  subdev[i]->writesize ||
 		    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 ||
-		    !concat->mtd.read_ecc  != !subdev[i]->read_ecc ||
-		    !concat->mtd.write_ecc != !subdev[i]->write_ecc ||
 		    !concat->mtd.read_oob  != !subdev[i]->read_oob ||
 		    !concat->mtd.read_oob  != !subdev[i]->read_oob ||
 		    !concat->mtd.write_oob != !subdev[i]->write_oob) {
 		    !concat->mtd.write_oob != !subdev[i]->write_oob) {
 			kfree(concat);
 			kfree(concat);
@@ -734,14 +778,11 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to c
 
 
 	}
 	}
 
 
+	concat->mtd.ecclayout = subdev[0]->ecclayout;
+
 	concat->num_subdev = num_devs;
 	concat->num_subdev = num_devs;
 	concat->mtd.name = name;
 	concat->mtd.name = name;
 
 
-	/*
-	 * NOTE: for now, we do not provide any readv()/writev() methods
-	 *       because they are messy to implement and they are not
-	 *       used to a great extent anyway.
-	 */
 	concat->mtd.erase = concat_erase;
 	concat->mtd.erase = concat_erase;
 	concat->mtd.read = concat_read;
 	concat->mtd.read = concat_read;
 	concat->mtd.write = concat_write;
 	concat->mtd.write = concat_write;

+ 1 - 32
drivers/mtd/mtdcore.c

@@ -47,6 +47,7 @@ int add_mtd_device(struct mtd_info *mtd)
 {
 {
 	int i;
 	int i;
 
 
+	BUG_ON(mtd->writesize == 0);
 	mutex_lock(&mtd_table_mutex);
 	mutex_lock(&mtd_table_mutex);
 
 
 	for (i=0; i < MAX_MTD_DEVICES; i++)
 	for (i=0; i < MAX_MTD_DEVICES; i++)
@@ -254,37 +255,6 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
 	return ret;
 	return ret;
 }
 }
 
 
-
-/* default_mtd_readv - default mtd readv method for MTD devices that dont
- *		       implement their own
- */
-
-int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
-		      unsigned long count, loff_t from, size_t *retlen)
-{
-	unsigned long i;
-	size_t totlen = 0, thislen;
-	int ret = 0;
-
-	if(!mtd->read) {
-		ret = -EIO;
-	} else {
-		for (i=0; i<count; i++) {
-			if (!vecs[i].iov_len)
-				continue;
-			ret = mtd->read(mtd, from, vecs[i].iov_len, &thislen, vecs[i].iov_base);
-			totlen += thislen;
-			if (ret || thislen != vecs[i].iov_len)
-				break;
-			from += vecs[i].iov_len;
-		}
-	}
-	if (retlen)
-		*retlen = totlen;
-	return ret;
-}
-
-
 EXPORT_SYMBOL(add_mtd_device);
 EXPORT_SYMBOL(add_mtd_device);
 EXPORT_SYMBOL(del_mtd_device);
 EXPORT_SYMBOL(del_mtd_device);
 EXPORT_SYMBOL(get_mtd_device);
 EXPORT_SYMBOL(get_mtd_device);
@@ -292,7 +262,6 @@ EXPORT_SYMBOL(put_mtd_device);
 EXPORT_SYMBOL(register_mtd_user);
 EXPORT_SYMBOL(register_mtd_user);
 EXPORT_SYMBOL(unregister_mtd_user);
 EXPORT_SYMBOL(unregister_mtd_user);
 EXPORT_SYMBOL(default_mtd_writev);
 EXPORT_SYMBOL(default_mtd_writev);
-EXPORT_SYMBOL(default_mtd_readv);
 
 
 #ifdef CONFIG_PROC_FS
 #ifdef CONFIG_PROC_FS
 
 

+ 55 - 116
drivers/mtd/mtdpart.c

@@ -51,16 +51,21 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
 			size_t *retlen, u_char *buf)
 			size_t *retlen, u_char *buf)
 {
 {
 	struct mtd_part *part = PART(mtd);
 	struct mtd_part *part = PART(mtd);
+	int res;
+
 	if (from >= mtd->size)
 	if (from >= mtd->size)
 		len = 0;
 		len = 0;
 	else if (from + len > mtd->size)
 	else if (from + len > mtd->size)
 		len = mtd->size - from;
 		len = mtd->size - from;
-	if (part->master->read_ecc == NULL)
-		return part->master->read (part->master, from + part->offset,
-					len, retlen, buf);
-	else
-		return part->master->read_ecc (part->master, from + part->offset,
-					len, retlen, buf, NULL, &mtd->oobinfo);
+	res = part->master->read (part->master, from + part->offset,
+				   len, retlen, buf);
+	if (unlikely(res)) {
+		if (res == -EUCLEAN)
+			mtd->ecc_stats.corrected++;
+		if (res == -EBADMSG)
+			mtd->ecc_stats.failed++;
+	}
+	return res;
 }
 }
 
 
 static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
 static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
@@ -74,6 +79,7 @@ static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
 	return part->master->point (part->master, from + part->offset,
 	return part->master->point (part->master, from + part->offset,
 				    len, retlen, buf);
 				    len, retlen, buf);
 }
 }
+
 static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
 static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
 {
 {
 	struct mtd_part *part = PART(mtd);
 	struct mtd_part *part = PART(mtd);
@@ -81,31 +87,25 @@ static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_
 	part->master->unpoint (part->master, addr, from + part->offset, len);
 	part->master->unpoint (part->master, addr, from + part->offset, len);
 }
 }
 
 
-
-static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
-			size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel)
+static int part_read_oob(struct mtd_info *mtd, loff_t from,
+			 struct mtd_oob_ops *ops)
 {
 {
 	struct mtd_part *part = PART(mtd);
 	struct mtd_part *part = PART(mtd);
-	if (oobsel == NULL)
-		oobsel = &mtd->oobinfo;
-	if (from >= mtd->size)
-		len = 0;
-	else if (from + len > mtd->size)
-		len = mtd->size - from;
-	return part->master->read_ecc (part->master, from + part->offset,
-					len, retlen, buf, eccbuf, oobsel);
-}
+	int res;
 
 
-static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
-			size_t *retlen, u_char *buf)
-{
-	struct mtd_part *part = PART(mtd);
 	if (from >= mtd->size)
 	if (from >= mtd->size)
-		len = 0;
-	else if (from + len > mtd->size)
-		len = mtd->size - from;
-	return part->master->read_oob (part->master, from + part->offset,
-					len, retlen, buf);
+		return -EINVAL;
+	if (from + ops->len > mtd->size)
+		return -EINVAL;
+	res = part->master->read_oob(part->master, from + part->offset, ops);
+
+	if (unlikely(res)) {
+		if (res == -EUCLEAN)
+			mtd->ecc_stats.corrected++;
+		if (res == -EBADMSG)
+			mtd->ecc_stats.failed++;
+	}
+	return res;
 }
 }
 
 
 static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
 static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
@@ -148,44 +148,23 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
 		len = 0;
 		len = 0;
 	else if (to + len > mtd->size)
 	else if (to + len > mtd->size)
 		len = mtd->size - to;
 		len = mtd->size - to;
-	if (part->master->write_ecc == NULL)
-		return part->master->write (part->master, to + part->offset,
-					len, retlen, buf);
-	else
-		return part->master->write_ecc (part->master, to + part->offset,
-					len, retlen, buf, NULL, &mtd->oobinfo);
-
+	return part->master->write (part->master, to + part->offset,
+				    len, retlen, buf);
 }
 }
 
 
-static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
-			size_t *retlen, const u_char *buf,
-			 u_char *eccbuf, struct nand_oobinfo *oobsel)
+static int part_write_oob(struct mtd_info *mtd, loff_t to,
+			 struct mtd_oob_ops *ops)
 {
 {
 	struct mtd_part *part = PART(mtd);
 	struct mtd_part *part = PART(mtd);
-	if (!(mtd->flags & MTD_WRITEABLE))
-		return -EROFS;
-	if (oobsel == NULL)
-		oobsel = &mtd->oobinfo;
-	if (to >= mtd->size)
-		len = 0;
-	else if (to + len > mtd->size)
-		len = mtd->size - to;
-	return part->master->write_ecc (part->master, to + part->offset,
-					len, retlen, buf, eccbuf, oobsel);
-}
 
 
-static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
-			size_t *retlen, const u_char *buf)
-{
-	struct mtd_part *part = PART(mtd);
 	if (!(mtd->flags & MTD_WRITEABLE))
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
 		return -EROFS;
+
 	if (to >= mtd->size)
 	if (to >= mtd->size)
-		len = 0;
-	else if (to + len > mtd->size)
-		len = mtd->size - to;
-	return part->master->write_oob (part->master, to + part->offset,
-					len, retlen, buf);
+		return -EINVAL;
+	if (to + ops->len > mtd->size)
+		return -EINVAL;
+	return part->master->write_oob(part->master, to + part->offset, ops);
 }
 }
 
 
 static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
 static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
@@ -208,52 +187,8 @@ static int part_writev (struct mtd_info *mtd,  const struct kvec *vecs,
 	struct mtd_part *part = PART(mtd);
 	struct mtd_part *part = PART(mtd);
 	if (!(mtd->flags & MTD_WRITEABLE))
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
 		return -EROFS;
-	if (part->master->writev_ecc == NULL)
-		return part->master->writev (part->master, vecs, count,
+	return part->master->writev (part->master, vecs, count,
 					to + part->offset, retlen);
 					to + part->offset, retlen);
-	else
-		return part->master->writev_ecc (part->master, vecs, count,
-					to + part->offset, retlen,
-					NULL, &mtd->oobinfo);
-}
-
-static int part_readv (struct mtd_info *mtd,  struct kvec *vecs,
-			 unsigned long count, loff_t from, size_t *retlen)
-{
-	struct mtd_part *part = PART(mtd);
-	if (part->master->readv_ecc == NULL)
-		return part->master->readv (part->master, vecs, count,
-					from + part->offset, retlen);
-	else
-		return part->master->readv_ecc (part->master, vecs, count,
-					from + part->offset, retlen,
-					NULL, &mtd->oobinfo);
-}
-
-static int part_writev_ecc (struct mtd_info *mtd,  const struct kvec *vecs,
-			 unsigned long count, loff_t to, size_t *retlen,
-			 u_char *eccbuf,  struct nand_oobinfo *oobsel)
-{
-	struct mtd_part *part = PART(mtd);
-	if (!(mtd->flags & MTD_WRITEABLE))
-		return -EROFS;
-	if (oobsel == NULL)
-		oobsel = &mtd->oobinfo;
-	return part->master->writev_ecc (part->master, vecs, count,
-					to + part->offset, retlen,
-					eccbuf, oobsel);
-}
-
-static int part_readv_ecc (struct mtd_info *mtd,  struct kvec *vecs,
-			 unsigned long count, loff_t from, size_t *retlen,
-			 u_char *eccbuf,  struct nand_oobinfo *oobsel)
-{
-	struct mtd_part *part = PART(mtd);
-	if (oobsel == NULL)
-		oobsel = &mtd->oobinfo;
-	return part->master->readv_ecc (part->master, vecs, count,
-					from + part->offset, retlen,
-					eccbuf, oobsel);
 }
 }
 
 
 static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
 static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
@@ -329,12 +264,17 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs)
 static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
 static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
 {
 {
 	struct mtd_part *part = PART(mtd);
 	struct mtd_part *part = PART(mtd);
+	int res;
+
 	if (!(mtd->flags & MTD_WRITEABLE))
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
 		return -EROFS;
 	if (ofs >= mtd->size)
 	if (ofs >= mtd->size)
 		return -EINVAL;
 		return -EINVAL;
 	ofs += part->offset;
 	ofs += part->offset;
-	return part->master->block_markbad(part->master, ofs);
+	res = part->master->block_markbad(part->master, ofs);
+	if (!res)
+		mtd->ecc_stats.badblocks++;
+	return res;
 }
 }
 
 
 /*
 /*
@@ -398,7 +338,7 @@ int add_mtd_partitions(struct mtd_info *master,
 		slave->mtd.type = master->type;
 		slave->mtd.type = master->type;
 		slave->mtd.flags = master->flags & ~parts[i].mask_flags;
 		slave->mtd.flags = master->flags & ~parts[i].mask_flags;
 		slave->mtd.size = parts[i].size;
 		slave->mtd.size = parts[i].size;
-		slave->mtd.oobblock = master->oobblock;
+		slave->mtd.writesize = master->writesize;
 		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;
@@ -415,10 +355,6 @@ int add_mtd_partitions(struct mtd_info *master,
 			slave->mtd.unpoint = part_unpoint;
 			slave->mtd.unpoint = part_unpoint;
 		}
 		}
 
 
-		if (master->read_ecc)
-			slave->mtd.read_ecc = part_read_ecc;
-		if (master->write_ecc)
-			slave->mtd.write_ecc = part_write_ecc;
 		if (master->read_oob)
 		if (master->read_oob)
 			slave->mtd.read_oob = part_read_oob;
 			slave->mtd.read_oob = part_read_oob;
 		if (master->write_oob)
 		if (master->write_oob)
@@ -443,12 +379,6 @@ int add_mtd_partitions(struct mtd_info *master,
 		}
 		}
 		if (master->writev)
 		if (master->writev)
 			slave->mtd.writev = part_writev;
 			slave->mtd.writev = part_writev;
-		if (master->readv)
-			slave->mtd.readv = part_readv;
-		if (master->writev_ecc)
-			slave->mtd.writev_ecc = part_writev_ecc;
-		if (master->readv_ecc)
-			slave->mtd.readv_ecc = part_readv_ecc;
 		if (master->lock)
 		if (master->lock)
 			slave->mtd.lock = part_lock;
 			slave->mtd.lock = part_lock;
 		if (master->unlock)
 		if (master->unlock)
@@ -528,8 +458,17 @@ int add_mtd_partitions(struct mtd_info *master,
 				parts[i].name);
 				parts[i].name);
 		}
 		}
 
 
-		/* copy oobinfo from master */
-		memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo));
+		slave->mtd.ecclayout = master->ecclayout;
+		if (master->block_isbad) {
+			uint32_t offs = 0;
+
+			while(offs < slave->mtd.size) {
+				if (master->block_isbad(master,
+							offs + slave->offset))
+					slave->mtd.ecc_stats.badblocks++;
+				offs += slave->mtd.erasesize;
+			}
+		}
 
 
 		if(parts[i].mtdp)
 		if(parts[i].mtdp)
 		{	/* store the object pointer (caller may or may not register it */
 		{	/* store the object pointer (caller may or may not register it */

+ 53 - 4
drivers/mtd/nand/Kconfig

@@ -23,6 +23,14 @@ config MTD_NAND_VERIFY_WRITE
 	  device thinks the write was successful, a bit could have been
 	  device thinks the write was successful, a bit could have been
 	  flipped accidentaly due to device wear or something else.
 	  flipped accidentaly due to device wear or something else.
 
 
+config MTD_NAND_ECC_SMC
+	bool "NAND ECC Smart Media byte order"
+	depends on MTD_NAND
+	default n
+	help
+	  Software ECC according to the Smart Media Specification.
+	  The original Linux implementation had byte 0 and 1 swapped.
+
 config MTD_NAND_AUTCPU12
 config MTD_NAND_AUTCPU12
 	tristate "SmartMediaCard on autronix autcpu12 board"
 	tristate "SmartMediaCard on autronix autcpu12 board"
 	depends on MTD_NAND && ARCH_AUTCPU12
 	depends on MTD_NAND && ARCH_AUTCPU12
@@ -49,12 +57,24 @@ config MTD_NAND_SPIA
 	help
 	help
 	  If you had to ask, you don't have one. Say 'N'.
 	  If you had to ask, you don't have one. Say 'N'.
 
 
+config MTD_NAND_AMS_DELTA
+	tristate "NAND Flash device on Amstrad E3"
+	depends on MACH_AMS_DELTA && MTD_NAND
+	help
+	  Support for NAND flash on Amstrad E3 (Delta).
+
 config MTD_NAND_TOTO
 config MTD_NAND_TOTO
 	tristate "NAND Flash device on TOTO board"
 	tristate "NAND Flash device on TOTO board"
-	depends on ARCH_OMAP && MTD_NAND
+	depends on ARCH_OMAP && MTD_NAND && BROKEN
 	help
 	help
 	  Support for NAND flash on Texas Instruments Toto platform.
 	  Support for NAND flash on Texas Instruments Toto platform.
 
 
+config MTD_NAND_TS7250
+	tristate "NAND Flash device on TS-7250 board"
+	depends on MACH_TS72XX && MTD_NAND
+	help
+	  Support for NAND flash on Technologic Systems TS-7250 platform.
+
 config MTD_NAND_IDS
 config MTD_NAND_IDS
 	tristate
 	tristate
 
 
@@ -76,7 +96,7 @@ config MTD_NAND_RTC_FROM4
 
 
 config MTD_NAND_PPCHAMELEONEVB
 config MTD_NAND_PPCHAMELEONEVB
 	tristate "NAND Flash device on PPChameleonEVB board"
 	tristate "NAND Flash device on PPChameleonEVB board"
-	depends on PPCHAMELEONEVB && MTD_NAND
+	depends on PPCHAMELEONEVB && MTD_NAND && BROKEN
 	help
 	help
 	  This enables the NAND flash driver on the PPChameleon EVB Board.
 	  This enables the NAND flash driver on the PPChameleon EVB Board.
 
 
@@ -87,7 +107,7 @@ config MTD_NAND_S3C2410
 	  This enables the NAND flash controller on the S3C2410 and S3C2440
 	  This enables the NAND flash controller on the S3C2410 and S3C2440
 	  SoCs
 	  SoCs
 
 
-	  No board specfic support is done by this driver, each board
+	  No board specific support is done by this driver, each board
 	  must advertise a platform_device for the driver to attach.
 	  must advertise a platform_device for the driver to attach.
 
 
 config MTD_NAND_S3C2410_DEBUG
 config MTD_NAND_S3C2410_DEBUG
@@ -109,6 +129,22 @@ config MTD_NAND_S3C2410_HWECC
 	  currently not be able to switch to software, as there is no
 	  currently not be able to switch to software, as there is no
 	  implementation for ECC method used by the S3C2410
 	  implementation for ECC method used by the S3C2410
 
 
+config MTD_NAND_NDFC
+	tristate "NDFC NanD Flash Controller"
+	depends on MTD_NAND && 44x
+	help
+	 NDFC Nand Flash Controllers are integrated in EP44x SoCs
+
+config MTD_NAND_S3C2410_CLKSTOP
+	bool "S3C2410 NAND IDLE clock stop"
+	depends on MTD_NAND_S3C2410
+	default n
+	help
+	  Stop the clock to the NAND controller when there is no chip
+	  selected to save power. This will mean there is a small delay
+	  when the is NAND chip selected or released, but will save
+	  approximately 5mA of power when there is nothing happening.
+
 config MTD_NAND_DISKONCHIP
 config MTD_NAND_DISKONCHIP
 	tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
 	tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
 	depends on MTD_NAND && EXPERIMENTAL
 	depends on MTD_NAND && EXPERIMENTAL
@@ -183,11 +219,24 @@ 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_CS553X
+	tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
+	depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH)
+	help
+	  The CS553x companion chips for the AMD Geode processor
+	  include NAND flash controllers with built-in hardware ECC
+	  capabilities; enabling this option will allow you to use
+	  these. The driver will check the MSRs to verify that the
+	  controller is enabled for NAND, and currently requires that
+	  the controller be in MMIO mode.
+
+	  If you say "m", the module will be called "cs553x_nand.ko".
+
 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
 	help
 	help
-	  The simulator may simulate verious NAND flash chips for the
+	  The simulator may simulate various NAND flash chips for the
 	  MTD nand layer.
 	  MTD nand layer.
 
 
 endmenu
 endmenu

+ 4 - 0
drivers/mtd/nand/Makefile

@@ -7,6 +7,7 @@ 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_SPIA)		+= spia.o
 obj-$(CONFIG_MTD_NAND_SPIA)		+= spia.o
+obj-$(CONFIG_MTD_NAND_AMS_DELTA)	+= ams-delta.o
 obj-$(CONFIG_MTD_NAND_TOTO)		+= toto.o
 obj-$(CONFIG_MTD_NAND_TOTO)		+= toto.o
 obj-$(CONFIG_MTD_NAND_AUTCPU12)		+= autcpu12.o
 obj-$(CONFIG_MTD_NAND_AUTCPU12)		+= autcpu12.o
 obj-$(CONFIG_MTD_NAND_EDB7312)		+= edb7312.o
 obj-$(CONFIG_MTD_NAND_EDB7312)		+= edb7312.o
@@ -17,6 +18,9 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP)	+= diskonchip.o
 obj-$(CONFIG_MTD_NAND_H1900)		+= h1910.o
 obj-$(CONFIG_MTD_NAND_H1900)		+= h1910.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)	+= rtc_from4.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)	+= rtc_from4.o
 obj-$(CONFIG_MTD_NAND_SHARPSL)		+= sharpsl.o
 obj-$(CONFIG_MTD_NAND_SHARPSL)		+= sharpsl.o
+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_NDFC)		+= ndfc.o
 
 
 nand-objs = nand_base.o nand_bbt.o
 nand-objs = nand_base.o nand_bbt.o

+ 237 - 0
drivers/mtd/nand/ams-delta.c

@@ -0,0 +1,237 @@
+/*
+ *  drivers/mtd/nand/ams-delta.c
+ *
+ *  Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
+ *
+ *  Derived from drivers/mtd/toto.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device found on the
+ *   Amstrad E3 (Delta).
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/sizes.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/board-ams-delta.h>
+
+/*
+ * MTD structure for E3 (Delta)
+ */
+static struct mtd_info *ams_delta_mtd = NULL;
+
+#define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP)
+
+/*
+ * Define partitions for flash devices
+ */
+
+static struct mtd_partition partition_info[] = {
+	{ .name		= "Kernel",
+	  .offset	= 0,
+	  .size		= 3 * SZ_1M + SZ_512K },
+	{ .name		= "u-boot",
+	  .offset	= 3 * SZ_1M + SZ_512K,
+	  .size		= SZ_256K },
+	{ .name		= "u-boot params",
+	  .offset	= 3 * SZ_1M + SZ_512K + SZ_256K,
+	  .size		= SZ_256K },
+	{ .name		= "Amstrad LDR",
+	  .offset	= 4 * SZ_1M,
+	  .size		= SZ_256K },
+	{ .name		= "File system",
+	  .offset	= 4 * SZ_1M + 1 * SZ_256K,
+	  .size		= 27 * SZ_1M },
+	{ .name		= "PBL reserved",
+	  .offset	= 32 * SZ_1M - 3 * SZ_256K,
+	  .size		=  3 * SZ_256K },
+};
+
+static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+
+	omap_writew(0, (OMAP_MPUIO_BASE + OMAP_MPUIO_IO_CNTL));
+	omap_writew(byte, this->IO_ADDR_W);
+	ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, 0);
+	ndelay(40);
+	ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE,
+			       AMS_DELTA_LATCH2_NAND_NWE);
+}
+
+static u_char ams_delta_read_byte(struct mtd_info *mtd)
+{
+	u_char res;
+	struct nand_chip *this = mtd->priv;
+
+	ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, 0);
+	ndelay(40);
+	omap_writew(~0, (OMAP_MPUIO_BASE + OMAP_MPUIO_IO_CNTL));
+	res = omap_readw(this->IO_ADDR_R);
+	ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE,
+			       AMS_DELTA_LATCH2_NAND_NRE);
+
+	return res;
+}
+
+static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
+				int len)
+{
+	int i;
+
+	for (i=0; i<len; i++)
+		ams_delta_write_byte(mtd, buf[i]);
+}
+
+static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+
+	for (i=0; i<len; i++)
+		buf[i] = ams_delta_read_byte(mtd);
+}
+
+static int ams_delta_verify_buf(struct mtd_info *mtd, const u_char *buf,
+				int len)
+{
+	int i;
+
+	for (i=0; i<len; i++)
+		if (buf[i] != ams_delta_read_byte(mtd))
+			return -EFAULT;
+
+	return 0;
+}
+
+/*
+ * Command control function
+ *
+ * ctrl:
+ * NAND_NCE: bit 0 -> bit 2
+ * NAND_CLE: bit 1 -> bit 7
+ * NAND_ALE: bit 2 -> bit 6
+ */
+static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
+				unsigned int ctrl)
+{
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned long bits;
+
+		bits = (~ctrl & NAND_NCE) << 2;
+		bits |= (ctrl & NAND_CLE) << 7;
+		bits |= (ctrl & NAND_ALE) << 6;
+
+		ams_delta_latch2_write(0xC2, bits);
+	}
+
+	if (cmd != NAND_CMD_NONE)
+		ams_delta_write_byte(mtd, cmd);
+}
+
+static int ams_delta_nand_ready(struct mtd_info *mtd)
+{
+	return omap_get_gpio_datain(AMS_DELTA_GPIO_PIN_NAND_RB);
+}
+
+/*
+ * Main initialization routine
+ */
+static int __init ams_delta_init(void)
+{
+	struct nand_chip *this;
+	int err = 0;
+
+	/* Allocate memory for MTD device structure and private data */
+	ams_delta_mtd = kmalloc(sizeof(struct mtd_info) +
+				sizeof(struct nand_chip), GFP_KERNEL);
+	if (!ams_delta_mtd) {
+		printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	ams_delta_mtd->owner = THIS_MODULE;
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&ams_delta_mtd[1]);
+
+	/* Initialize structures */
+	memset(ams_delta_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	ams_delta_mtd->priv = this;
+
+	/* Set address of NAND IO lines */
+	this->IO_ADDR_R = (OMAP_MPUIO_BASE + OMAP_MPUIO_INPUT_LATCH);
+	this->IO_ADDR_W = (OMAP_MPUIO_BASE + OMAP_MPUIO_OUTPUT);
+	this->read_byte = ams_delta_read_byte;
+	this->write_buf = ams_delta_write_buf;
+	this->read_buf = ams_delta_read_buf;
+	this->verify_buf = ams_delta_verify_buf;
+	this->cmd_ctrl = ams_delta_hwcontrol;
+	if (!omap_request_gpio(AMS_DELTA_GPIO_PIN_NAND_RB)) {
+		this->dev_ready = ams_delta_nand_ready;
+	} else {
+		this->dev_ready = NULL;
+		printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n");
+	}
+	/* 25 us command delay time */
+	this->chip_delay = 30;
+	this->ecc.mode = NAND_ECC_SOFT;
+
+	/* Set chip enabled, but  */
+	ams_delta_latch2_write(NAND_MASK, AMS_DELTA_LATCH2_NAND_NRE |
+					  AMS_DELTA_LATCH2_NAND_NWE |
+					  AMS_DELTA_LATCH2_NAND_NCE |
+					  AMS_DELTA_LATCH2_NAND_NWP);
+
+	/* Scan to find existance of the device */
+	if (nand_scan(ams_delta_mtd, 1)) {
+		err = -ENXIO;
+		goto out_mtd;
+	}
+
+	/* Register the partitions */
+	add_mtd_partitions(ams_delta_mtd, partition_info,
+			   ARRAY_SIZE(partition_info));
+
+	goto out;
+
+ out_mtd:
+	kfree(ams_delta_mtd);
+ out:
+	return err;
+}
+
+module_init(ams_delta_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit ams_delta_cleanup(void)
+{
+	/* Release resources, unregister device */
+	nand_release(ams_delta_mtd);
+
+	/* Free the MTD device structure */
+	kfree(ams_delta_mtd);
+}
+module_exit(ams_delta_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
+MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");

+ 228 - 93
drivers/mtd/nand/au1550nd.c

@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/interrupt.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/partitions.h>
@@ -38,22 +39,21 @@
  */
  */
 static struct mtd_info *au1550_mtd = NULL;
 static struct mtd_info *au1550_mtd = NULL;
 static void __iomem *p_nand;
 static void __iomem *p_nand;
-static int nand_width = 1; /* default x8*/
+static int nand_width = 1;	/* default x8 */
+static void (*au1550_write_byte)(struct mtd_info *, u_char);
 
 
 /*
 /*
  * Define partitions for flash device
  * Define partitions for flash device
  */
  */
 static const struct mtd_partition partition_info[] = {
 static const struct mtd_partition partition_info[] = {
 	{
 	{
-		.name 	= "NAND FS 0",
-	  	.offset = 0,
-	  	.size 	= 8*1024*1024
-	},
+	 .name = "NAND FS 0",
+	 .offset = 0,
+	 .size = 8 * 1024 * 1024},
 	{
 	{
-		.name 	= "NAND FS 1",
-		.offset =  MTDPART_OFS_APPEND,
- 		.size 	=    MTDPART_SIZ_FULL
-	}
+	 .name = "NAND FS 1",
+	 .offset = MTDPART_OFS_APPEND,
+	 .size = MTDPART_SIZ_FULL}
 };
 };
 
 
 /**
 /**
@@ -129,21 +129,6 @@ static u16 au_read_word(struct mtd_info *mtd)
 	return ret;
 	return ret;
 }
 }
 
 
-/**
- * au_write_word -  write one word to the chip
- * @mtd:	MTD device structure
- * @word:	data word to write
- *
- *  write function for 16bit buswith without
- * endianess conversion
- */
-static void au_write_word(struct mtd_info *mtd, u16 word)
-{
-	struct nand_chip *this = mtd->priv;
-	writew(word, this->IO_ADDR_W);
-	au_sync();
-}
-
 /**
 /**
  * au_write_buf -  write buffer to chip
  * au_write_buf -  write buffer to chip
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -157,7 +142,7 @@ static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 	int i;
 	int i;
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 
 
-	for (i=0; i<len; i++) {
+	for (i = 0; i < len; i++) {
 		writeb(buf[i], this->IO_ADDR_W);
 		writeb(buf[i], this->IO_ADDR_W);
 		au_sync();
 		au_sync();
 	}
 	}
@@ -176,7 +161,7 @@ static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 	int i;
 	int i;
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 
 
-	for (i=0; i<len; i++) {
+	for (i = 0; i < len; i++) {
 		buf[i] = readb(this->IO_ADDR_R);
 		buf[i] = readb(this->IO_ADDR_R);
 		au_sync();
 		au_sync();
 	}
 	}
@@ -195,7 +180,7 @@ static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
 	int i;
 	int i;
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 
 
-	for (i=0; i<len; i++) {
+	for (i = 0; i < len; i++) {
 		if (buf[i] != readb(this->IO_ADDR_R))
 		if (buf[i] != readb(this->IO_ADDR_R))
 			return -EFAULT;
 			return -EFAULT;
 		au_sync();
 		au_sync();
@@ -219,7 +204,7 @@ static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
 	u16 *p = (u16 *) buf;
 	u16 *p = (u16 *) buf;
 	len >>= 1;
 	len >>= 1;
 
 
-	for (i=0; i<len; i++) {
+	for (i = 0; i < len; i++) {
 		writew(p[i], this->IO_ADDR_W);
 		writew(p[i], this->IO_ADDR_W);
 		au_sync();
 		au_sync();
 	}
 	}
@@ -241,7 +226,7 @@ static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
 	u16 *p = (u16 *) buf;
 	u16 *p = (u16 *) buf;
 	len >>= 1;
 	len >>= 1;
 
 
-	for (i=0; i<len; i++) {
+	for (i = 0; i < len; i++) {
 		p[i] = readw(this->IO_ADDR_R);
 		p[i] = readw(this->IO_ADDR_R);
 		au_sync();
 		au_sync();
 	}
 	}
@@ -262,7 +247,7 @@ static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
 	u16 *p = (u16 *) buf;
 	u16 *p = (u16 *) buf;
 	len >>= 1;
 	len >>= 1;
 
 
-	for (i=0; i<len; i++) {
+	for (i = 0; i < len; i++) {
 		if (p[i] != readw(this->IO_ADDR_R))
 		if (p[i] != readw(this->IO_ADDR_R))
 			return -EFAULT;
 			return -EFAULT;
 		au_sync();
 		au_sync();
@@ -270,32 +255,52 @@ static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
 	return 0;
 	return 0;
 }
 }
 
 
+/* Select the chip by setting nCE to low */
+#define NAND_CTL_SETNCE		1
+/* Deselect the chip by setting nCE to high */
+#define NAND_CTL_CLRNCE		2
+/* Select the command latch by setting CLE to high */
+#define NAND_CTL_SETCLE		3
+/* Deselect the command latch by setting CLE to low */
+#define NAND_CTL_CLRCLE		4
+/* Select the address latch by setting ALE to high */
+#define NAND_CTL_SETALE		5
+/* Deselect the address latch by setting ALE to low */
+#define NAND_CTL_CLRALE		6
 
 
 static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
 static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
 {
 {
 	register struct nand_chip *this = mtd->priv;
 	register struct nand_chip *this = mtd->priv;
 
 
-	switch(cmd){
+	switch (cmd) {
 
 
-	case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break;
-	case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break;
+	case NAND_CTL_SETCLE:
+		this->IO_ADDR_W = p_nand + MEM_STNAND_CMD;
+		break;
+
+	case NAND_CTL_CLRCLE:
+		this->IO_ADDR_W = p_nand + MEM_STNAND_DATA;
+		break;
+
+	case NAND_CTL_SETALE:
+		this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR;
+		break;
 
 
-	case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break;
 	case NAND_CTL_CLRALE:
 	case NAND_CTL_CLRALE:
 		this->IO_ADDR_W = p_nand + MEM_STNAND_DATA;
 		this->IO_ADDR_W = p_nand + MEM_STNAND_DATA;
-		/* FIXME: Nobody knows why this is neccecary,
+		/* FIXME: Nobody knows why this is necessary,
 		 * but it works only that way */
 		 * but it works only that way */
 		udelay(1);
 		udelay(1);
 		break;
 		break;
 
 
 	case NAND_CTL_SETNCE:
 	case NAND_CTL_SETNCE:
 		/* assert (force assert) chip enable */
 		/* assert (force assert) chip enable */
-		au_writel((1<<(4+NAND_CS)) , MEM_STNDCTL); break;
+		au_writel((1 << (4 + NAND_CS)), MEM_STNDCTL);
 		break;
 		break;
 
 
 	case NAND_CTL_CLRNCE:
 	case NAND_CTL_CLRNCE:
- 		/* deassert chip enable */
-		au_writel(0, MEM_STNDCTL); break;
+		/* deassert chip enable */
+		au_writel(0, MEM_STNDCTL);
 		break;
 		break;
 	}
 	}
 
 
@@ -312,69 +317,200 @@ int au1550_device_ready(struct mtd_info *mtd)
 	return ret;
 	return ret;
 }
 }
 
 
+/**
+ * au1550_select_chip - control -CE line
+ *	Forbid driving -CE manually permitting the NAND controller to do this.
+ *	Keeping -CE asserted during the whole sector reads interferes with the
+ *	NOR flash and PCMCIA drivers as it causes contention on the static bus.
+ *	We only have to hold -CE low for the NAND read commands since the flash
+ *	chip needs it to be asserted during chip not ready time but the NAND
+ *	controller keeps it released.
+ *
+ * @mtd:	MTD device structure
+ * @chip:	chipnumber to select, -1 for deselect
+ */
+static void au1550_select_chip(struct mtd_info *mtd, int chip)
+{
+}
+
+/**
+ * au1550_command - Send command to NAND device
+ * @mtd:	MTD device structure
+ * @command:	the command to be sent
+ * @column:	the column address for this command, -1 if none
+ * @page_addr:	the page address for this command, -1 if none
+ */
+static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+	register struct nand_chip *this = mtd->priv;
+	int ce_override = 0, i;
+	ulong flags;
+
+	/* Begin command latch cycle */
+	au1550_hwcontrol(mtd, NAND_CTL_SETCLE);
+	/*
+	 * Write out the command to the device.
+	 */
+	if (command == NAND_CMD_SEQIN) {
+		int readcmd;
+
+		if (column >= mtd->writesize) {
+			/* OOB area */
+			column -= mtd->writesize;
+			readcmd = NAND_CMD_READOOB;
+		} else if (column < 256) {
+			/* First 256 bytes --> READ0 */
+			readcmd = NAND_CMD_READ0;
+		} else {
+			column -= 256;
+			readcmd = NAND_CMD_READ1;
+		}
+		au1550_write_byte(mtd, readcmd);
+	}
+	au1550_write_byte(mtd, command);
+
+	/* Set ALE and clear CLE to start address cycle */
+	au1550_hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+	if (column != -1 || page_addr != -1) {
+		au1550_hwcontrol(mtd, NAND_CTL_SETALE);
+
+		/* Serially input address */
+		if (column != -1) {
+			/* Adjust columns for 16 bit buswidth */
+			if (this->options & NAND_BUSWIDTH_16)
+				column >>= 1;
+			au1550_write_byte(mtd, column);
+		}
+		if (page_addr != -1) {
+			au1550_write_byte(mtd, (u8)(page_addr & 0xff));
+
+			if (command == NAND_CMD_READ0 ||
+			    command == NAND_CMD_READ1 ||
+			    command == NAND_CMD_READOOB) {
+				/*
+				 * NAND controller will release -CE after
+				 * the last address byte is written, so we'll
+				 * have to forcibly assert it. No interrupts
+				 * are allowed while we do this as we don't
+				 * want the NOR flash or PCMCIA drivers to
+				 * steal our precious bytes of data...
+				 */
+				ce_override = 1;
+				local_irq_save(flags);
+				au1550_hwcontrol(mtd, NAND_CTL_SETNCE);
+			}
+
+			au1550_write_byte(mtd, (u8)(page_addr >> 8));
+
+			/* One more address cycle for devices > 32MiB */
+			if (this->chipsize > (32 << 20))
+				au1550_write_byte(mtd, (u8)((page_addr >> 16) & 0x0f));
+		}
+		/* Latch in address */
+		au1550_hwcontrol(mtd, NAND_CTL_CLRALE);
+	}
+
+	/*
+	 * Program and erase have their own busy handlers.
+	 * Status and sequential in need no delay.
+	 */
+	switch (command) {
+
+	case NAND_CMD_PAGEPROG:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_STATUS:
+		return;
+
+	case NAND_CMD_RESET:
+		break;
+
+	case NAND_CMD_READ0:
+	case NAND_CMD_READ1:
+	case NAND_CMD_READOOB:
+		/* Check if we're really driving -CE low (just in case) */
+		if (unlikely(!ce_override))
+			break;
+
+		/* Apply a short delay always to ensure that we do wait tWB. */
+		ndelay(100);
+		/* Wait for a chip to become ready... */
+		for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i)
+			udelay(1);
+
+		/* Release -CE and re-enable interrupts. */
+		au1550_hwcontrol(mtd, NAND_CTL_CLRNCE);
+		local_irq_restore(flags);
+		return;
+	}
+	/* Apply this short delay always to ensure that we do wait tWB. */
+	ndelay(100);
+
+	while(!this->dev_ready(mtd));
+}
+
+
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-int __init au1xxx_nand_init (void)
+static int __init au1xxx_nand_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
-	u16 boot_swapboot = 0; /* default value */
+	u16 boot_swapboot = 0;	/* default value */
 	int retval;
 	int retval;
 	u32 mem_staddr;
 	u32 mem_staddr;
 	u32 nand_phys;
 	u32 nand_phys;
 
 
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	au1550_mtd = kmalloc (sizeof(struct mtd_info) +
-			sizeof (struct nand_chip), GFP_KERNEL);
+	au1550_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!au1550_mtd) {
 	if (!au1550_mtd) {
-		printk ("Unable to allocate NAND MTD dev structure.\n");
+		printk("Unable to allocate NAND MTD dev structure.\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&au1550_mtd[1]);
+	this = (struct nand_chip *)(&au1550_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) au1550_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(au1550_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	au1550_mtd->priv = this;
 	au1550_mtd->priv = this;
+	au1550_mtd->owner = THIS_MODULE;
 
 
 
 
-	/* disable interrupts */
-	au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL);
-
-	/* disable NAND boot */
-	au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL);
+	/* MEM_STNDCTL: disable ints, disable nand boot */
+	au_writel(0, MEM_STNDCTL);
 
 
 #ifdef CONFIG_MIPS_PB1550
 #ifdef CONFIG_MIPS_PB1550
 	/* set gpio206 high */
 	/* set gpio206 high */
-	au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR);
+	au_writel(au_readl(GPIO2_DIR) & ~(1 << 6), GPIO2_DIR);
 
 
-	boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) |
-		((bcsr->status >> 6)  & 0x1);
+	boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr->status >> 6) & 0x1);
 	switch (boot_swapboot) {
 	switch (boot_swapboot) {
-		case 0:
-		case 2:
-		case 8:
-		case 0xC:
-		case 0xD:
-			/* x16 NAND Flash */
-			nand_width = 0;
-			break;
-		case 1:
-		case 9:
-		case 3:
-		case 0xE:
-		case 0xF:
-			/* x8 NAND Flash */
-			nand_width = 1;
-			break;
-		default:
-			printk("Pb1550 NAND: bad boot:swap\n");
-			retval = -EINVAL;
-			goto outmem;
+	case 0:
+	case 2:
+	case 8:
+	case 0xC:
+	case 0xD:
+		/* x16 NAND Flash */
+		nand_width = 0;
+		break;
+	case 1:
+	case 9:
+	case 3:
+	case 0xE:
+	case 0xF:
+		/* x8 NAND Flash */
+		nand_width = 1;
+		break;
+	default:
+		printk("Pb1550 NAND: bad boot:swap\n");
+		retval = -EINVAL;
+		goto outmem;
 	}
 	}
 #endif
 #endif
 
 
@@ -424,21 +560,22 @@ int __init au1xxx_nand_init (void)
 
 
 	/* make controller and MTD agree */
 	/* make controller and MTD agree */
 	if (NAND_CS == 0)
 	if (NAND_CS == 0)
-		nand_width = au_readl(MEM_STCFG0) & (1<<22);
+		nand_width = au_readl(MEM_STCFG0) & (1 << 22);
 	if (NAND_CS == 1)
 	if (NAND_CS == 1)
-		nand_width = au_readl(MEM_STCFG1) & (1<<22);
+		nand_width = au_readl(MEM_STCFG1) & (1 << 22);
 	if (NAND_CS == 2)
 	if (NAND_CS == 2)
-		nand_width = au_readl(MEM_STCFG2) & (1<<22);
+		nand_width = au_readl(MEM_STCFG2) & (1 << 22);
 	if (NAND_CS == 3)
 	if (NAND_CS == 3)
-		nand_width = au_readl(MEM_STCFG3) & (1<<22);
-
+		nand_width = au_readl(MEM_STCFG3) & (1 << 22);
 
 
 	/* Set address of hardware control function */
 	/* Set address of hardware control function */
-	this->hwcontrol = au1550_hwcontrol;
 	this->dev_ready = au1550_device_ready;
 	this->dev_ready = au1550_device_ready;
+	this->select_chip = au1550_select_chip;
+	this->cmdfunc = au1550_command;
+
 	/* 30 us command delay time */
 	/* 30 us command delay time */
 	this->chip_delay = 30;
 	this->chip_delay = 30;
-	this->eccmode = NAND_ECC_SOFT;
+	this->ecc.mode = NAND_ECC_SOFT;
 
 
 	this->options = NAND_NO_AUTOINCR;
 	this->options = NAND_NO_AUTOINCR;
 
 
@@ -446,15 +583,14 @@ int __init au1xxx_nand_init (void)
 		this->options |= NAND_BUSWIDTH_16;
 		this->options |= NAND_BUSWIDTH_16;
 
 
 	this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte;
 	this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte;
-	this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
-	this->write_word = au_write_word;
+	au1550_write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
 	this->read_word = au_read_word;
 	this->read_word = au_read_word;
 	this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf;
 	this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf;
 	this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf;
 	this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf;
 	this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf;
 	this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf;
 
 
 	/* Scan to find existence of the device */
 	/* Scan to find existence of the device */
-	if (nand_scan (au1550_mtd, 1)) {
+	if (nand_scan(au1550_mtd, 1)) {
 		retval = -ENXIO;
 		retval = -ENXIO;
 		goto outio;
 		goto outio;
 	}
 	}
@@ -465,10 +601,10 @@ int __init au1xxx_nand_init (void)
 	return 0;
 	return 0;
 
 
  outio:
  outio:
-	iounmap ((void *)p_nand);
+	iounmap((void *)p_nand);
 
 
  outmem:
  outmem:
-	kfree (au1550_mtd);
+	kfree(au1550_mtd);
 	return retval;
 	return retval;
 }
 }
 
 
@@ -477,22 +613,21 @@ module_init(au1xxx_nand_init);
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-#ifdef MODULE
-static void __exit au1550_cleanup (void)
+static void __exit au1550_cleanup(void)
 {
 {
-	struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
+	struct nand_chip *this = (struct nand_chip *)&au1550_mtd[1];
 
 
 	/* Release resources, unregister device */
 	/* Release resources, unregister device */
-	nand_release (au1550_mtd);
+	nand_release(au1550_mtd);
 
 
 	/* Free the MTD device structure */
 	/* Free the MTD device structure */
-	kfree (au1550_mtd);
+	kfree(au1550_mtd);
 
 
 	/* Unmap */
 	/* Unmap */
-	iounmap ((void *)p_nand);
+	iounmap((void *)p_nand);
 }
 }
+
 module_exit(au1550_cleanup);
 module_exit(au1550_cleanup);
-#endif
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Embedded Edge, LLC");
 MODULE_AUTHOR("Embedded Edge, LLC");

+ 71 - 54
drivers/mtd/nand/autcpu12.c

@@ -4,7 +4,7 @@
  *  Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
  *  Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
  *
  *
  *  Derived from drivers/mtd/spia.c
  *  Derived from drivers/mtd/spia.c
- * 	 Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *	 Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *
  *
  * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $
  * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $
  *
  *
@@ -42,12 +42,7 @@
  * MTD structure for AUTCPU12 board
  * MTD structure for AUTCPU12 board
  */
  */
 static struct mtd_info *autcpu12_mtd = NULL;
 static struct mtd_info *autcpu12_mtd = NULL;
-
-static int autcpu12_io_base = CS89712_VIRT_BASE;
-static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC;
-static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET;
-static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET;
-static void __iomem * autcpu12_fio_base;
+static void __iomem *autcpu12_fio_base;
 
 
 /*
 /*
  * Define partitions for flash devices
  * Define partitions for flash devices
@@ -94,108 +89,131 @@ static struct mtd_partition partition_info128k[] = {
 #define NUM_PARTITIONS128K 2
 #define NUM_PARTITIONS128K 2
 /*
 /*
  *	hardware specific access to control-lines
  *	hardware specific access to control-lines
-*/
-static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
+ *
+ *	ALE bit 4 autcpu12_pedr
+ *	CLE bit 5 autcpu12_pedr
+ *	NCE bit 0 fio_ctrl
+ *
+ */
+static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd,
+			       unsigned int ctrl)
 {
 {
+	struct nand_chip *chip = mtd->priv;
 
 
-	switch(cmd){
-
-		case NAND_CTL_SETCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |=  AUTCPU12_SMC_CLE; break;
-		case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_CLE; break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		void __iomem *addr
+		unsigned char bits;
 
 
-		case NAND_CTL_SETALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |=  AUTCPU12_SMC_ALE; break;
-		case NAND_CTL_CLRALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_ALE; break;
+		addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET;
+		bits = (ctrl & NAND_CLE) << 4;
+		bits |= (ctrl & NAND_ALE) << 2;
+		writeb((readb(addr) & ~0x30) | bits, addr);
 
 
-		case NAND_CTL_SETNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x01; break;
-		case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x00; break;
+		addr = autcpu12_fio_base + AUTCPU12_SMC_SELECT_OFFSET;
+		writeb((readb(addr) & ~0x1) | (ctrl & NAND_NCE), addr);
 	}
 	}
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 }
 
 
 /*
 /*
-*	read device ready pin
-*/
+ *	read device ready pin
+ */
 int autcpu12_device_ready(struct mtd_info *mtd)
 int autcpu12_device_ready(struct mtd_info *mtd)
 {
 {
+	void __iomem *addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET;
 
 
-	return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0;
-
+	return readb(addr) & AUTCPU12_SMC_RDY;
 }
 }
 
 
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-int __init autcpu12_init (void)
+static int __init autcpu12_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
 	int err = 0;
 	int err = 0;
 
 
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	autcpu12_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
-				GFP_KERNEL);
+	autcpu12_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
+			       GFP_KERNEL);
 	if (!autcpu12_mtd) {
 	if (!autcpu12_mtd) {
-		printk ("Unable to allocate AUTCPU12 NAND MTD device structure.\n");
+		printk("Unable to allocate AUTCPU12 NAND MTD device structure.\n");
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto out;
 		goto out;
 	}
 	}
 
 
 	/* map physical adress */
 	/* map physical adress */
-	autcpu12_fio_base = ioremap(autcpu12_fio_pbase,SZ_1K);
-	if(!autcpu12_fio_base){
+	autcpu12_fio_base = ioremap(AUTCPU12_PHYS_SMC, SZ_1K);
+	if (!autcpu12_fio_base) {
 		printk("Ioremap autcpu12 SmartMedia Card failed\n");
 		printk("Ioremap autcpu12 SmartMedia Card failed\n");
 		err = -EIO;
 		err = -EIO;
 		goto out_mtd;
 		goto out_mtd;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&autcpu12_mtd[1]);
+	this = (struct nand_chip *)(&autcpu12_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) autcpu12_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(autcpu12_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	autcpu12_mtd->priv = this;
 	autcpu12_mtd->priv = this;
+	autcpu12_mtd->owner = THIS_MODULE;
 
 
 	/* Set address of NAND IO lines */
 	/* Set address of NAND IO lines */
 	this->IO_ADDR_R = autcpu12_fio_base;
 	this->IO_ADDR_R = autcpu12_fio_base;
 	this->IO_ADDR_W = autcpu12_fio_base;
 	this->IO_ADDR_W = autcpu12_fio_base;
-	this->hwcontrol = autcpu12_hwcontrol;
+	this->cmd_ctrl = autcpu12_hwcontrol;
 	this->dev_ready = autcpu12_device_ready;
 	this->dev_ready = autcpu12_device_ready;
 	/* 20 us command delay time */
 	/* 20 us command delay time */
 	this->chip_delay = 20;
 	this->chip_delay = 20;
-	this->eccmode = NAND_ECC_SOFT;
+	this->ecc.mode = NAND_ECC_SOFT;
 
 
 	/* Enable the following for a flash based bad block table */
 	/* Enable the following for a flash based bad block table */
 	/*
 	/*
-	this->options = NAND_USE_FLASH_BBT;
-	*/
+	   this->options = NAND_USE_FLASH_BBT;
+	 */
 	this->options = NAND_USE_FLASH_BBT;
 	this->options = NAND_USE_FLASH_BBT;
 
 
 	/* Scan to find existance of the device */
 	/* Scan to find existance of the device */
-	if (nand_scan (autcpu12_mtd, 1)) {
+	if (nand_scan(autcpu12_mtd, 1)) {
 		err = -ENXIO;
 		err = -ENXIO;
 		goto out_ior;
 		goto out_ior;
 	}
 	}
 
 
 	/* Register the partitions */
 	/* Register the partitions */
-	switch(autcpu12_mtd->size){
-		case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
-		case SZ_32M: add_mtd_partitions(autcpu12_mtd, partition_info32k, NUM_PARTITIONS32K); break;
-		case SZ_64M: add_mtd_partitions(autcpu12_mtd, partition_info64k, NUM_PARTITIONS64K); break;
-		case SZ_128M: add_mtd_partitions(autcpu12_mtd, partition_info128k, NUM_PARTITIONS128K); break;
-		default: {
-			printk ("Unsupported SmartMedia device\n");
+	switch (autcpu12_mtd->size) {
+		case SZ_16M:
+			add_mtd_partitions(autcpu12_mtd, partition_info16k,
+					   NUM_PARTITIONS16K);
+			break;
+		case SZ_32M:
+			add_mtd_partitions(autcpu12_mtd, partition_info32k,
+					   NUM_PARTITIONS32K);
+			break;
+		case SZ_64M:
+			add_mtd_partitions(autcpu12_mtd, partition_info64k,
+					   NUM_PARTITIONS64K);
+			break;
+		case SZ_128M:
+			add_mtd_partitions(autcpu12_mtd, partition_info128k,
+					   NUM_PARTITIONS128K);
+			break;
+		default:
+			printk("Unsupported SmartMedia device\n");
 			err = -ENXIO;
 			err = -ENXIO;
 			goto out_ior;
 			goto out_ior;
-		}
 	}
 	}
 	goto out;
 	goto out;
 
 
-out_ior:
-	iounmap((void *)autcpu12_fio_base);
-out_mtd:
-	kfree (autcpu12_mtd);
-out:
+ out_ior:
+	iounmap(autcpu12_fio_base);
+ out_mtd:
+	kfree(autcpu12_mtd);
+ out:
 	return err;
 	return err;
 }
 }
 
 
@@ -204,20 +222,19 @@ module_init(autcpu12_init);
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-#ifdef MODULE
-static void __exit autcpu12_cleanup (void)
+static void __exit autcpu12_cleanup(void)
 {
 {
 	/* Release resources, unregister device */
 	/* Release resources, unregister device */
-	nand_release (autcpu12_mtd);
+	nand_release(autcpu12_mtd);
 
 
 	/* unmap physical adress */
 	/* unmap physical adress */
-	iounmap((void *)autcpu12_fio_base);
+	iounmap(autcpu12_fio_base);
 
 
 	/* Free the MTD device structure */
 	/* Free the MTD device structure */
-	kfree (autcpu12_mtd);
+	kfree(autcpu12_mtd);
 }
 }
+
 module_exit(autcpu12_cleanup);
 module_exit(autcpu12_cleanup);
-#endif
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
 MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");

+ 353 - 0
drivers/mtd/nand/cs553x_nand.c

@@ -0,0 +1,353 @@
+/*
+ * drivers/mtd/nand/cs553x_nand.c
+ *
+ * (C) 2005, 2006 Red Hat Inc.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *	   Tom Sylla <tom.sylla@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash controller found on 
+ *   the AMD CS5535/CS5536 companion chipsets for the Geode processor.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/msr.h>
+#include <asm/io.h>
+
+#define NR_CS553X_CONTROLLERS	4
+
+#define MSR_DIVIL_GLD_CAP	0x51400000	/* DIVIL capabilitiies */
+#define CAP_CS5535		0x2df000ULL
+#define CAP_CS5536		0x5df500ULL
+
+/* NAND Timing MSRs */
+#define MSR_NANDF_DATA		0x5140001b	/* NAND Flash Data Timing MSR */
+#define MSR_NANDF_CTL		0x5140001c	/* NAND Flash Control Timing */
+#define MSR_NANDF_RSVD		0x5140001d	/* Reserved */
+
+/* NAND BAR MSRs */
+#define MSR_DIVIL_LBAR_FLSH0	0x51400010	/* Flash Chip Select 0 */
+#define MSR_DIVIL_LBAR_FLSH1	0x51400011	/* Flash Chip Select 1 */
+#define MSR_DIVIL_LBAR_FLSH2	0x51400012	/* Flash Chip Select 2 */
+#define MSR_DIVIL_LBAR_FLSH3	0x51400013	/* Flash Chip Select 3 */
+	/* Each made up of... */
+#define FLSH_LBAR_EN		(1ULL<<32)
+#define FLSH_NOR_NAND		(1ULL<<33)	/* 1 for NAND */
+#define FLSH_MEM_IO		(1ULL<<34)	/* 1 for MMIO */
+	/* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */
+	/* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */
+
+/* Pin function selection MSR (IDE vs. flash on the IDE pins) */
+#define MSR_DIVIL_BALL_OPTS	0x51400015
+#define PIN_OPT_IDE		(1<<0)	/* 0 for flash, 1 for IDE */
+
+/* Registers within the NAND flash controller BAR -- memory mapped */
+#define MM_NAND_DATA		0x00	/* 0 to 0x7ff, in fact */
+#define MM_NAND_CTL		0x800	/* Any even address 0x800-0x80e */
+#define MM_NAND_IO		0x801	/* Any odd address 0x801-0x80f */
+#define MM_NAND_STS		0x810
+#define MM_NAND_ECC_LSB		0x811
+#define MM_NAND_ECC_MSB		0x812
+#define MM_NAND_ECC_COL		0x813
+#define MM_NAND_LAC		0x814
+#define MM_NAND_ECC_CTL		0x815
+
+/* Registers within the NAND flash controller BAR -- I/O mapped */
+#define IO_NAND_DATA		0x00	/* 0 to 3, in fact */
+#define IO_NAND_CTL		0x04
+#define IO_NAND_IO		0x05
+#define IO_NAND_STS		0x06
+#define IO_NAND_ECC_CTL		0x08
+#define IO_NAND_ECC_LSB		0x09
+#define IO_NAND_ECC_MSB		0x0a
+#define IO_NAND_ECC_COL		0x0b
+#define IO_NAND_LAC		0x0c
+
+#define CS_NAND_CTL_DIST_EN	(1<<4)	/* Enable NAND Distract interrupt */
+#define CS_NAND_CTL_RDY_INT_MASK	(1<<3)	/* Enable RDY/BUSY# interrupt */
+#define CS_NAND_CTL_ALE		(1<<2)
+#define CS_NAND_CTL_CLE		(1<<1)
+#define CS_NAND_CTL_CE		(1<<0)	/* Keep low; 1 to reset */
+
+#define CS_NAND_STS_FLASH_RDY	(1<<3)
+#define CS_NAND_CTLR_BUSY	(1<<2)
+#define CS_NAND_CMD_COMP	(1<<1)
+#define CS_NAND_DIST_ST		(1<<0)
+
+#define CS_NAND_ECC_PARITY	(1<<2)
+#define CS_NAND_ECC_CLRECC	(1<<1)
+#define CS_NAND_ECC_ENECC	(1<<0)
+
+static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+
+	while (unlikely(len > 0x800)) {
+		memcpy_fromio(buf, this->IO_ADDR_R, 0x800);
+		buf += 0x800;
+		len -= 0x800;
+	}
+	memcpy_fromio(buf, this->IO_ADDR_R, len);
+}
+
+static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+
+	while (unlikely(len > 0x800)) {
+		memcpy_toio(this->IO_ADDR_R, buf, 0x800);
+		buf += 0x800;
+		len -= 0x800;
+	}
+	memcpy_toio(this->IO_ADDR_R, buf, len);
+}
+
+static unsigned char cs553x_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return readb(this->IO_ADDR_R);
+}
+
+static void cs553x_write_byte(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	int i = 100000;
+
+	while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) {
+		udelay(1);
+		i--;
+	}
+	writeb(byte, this->IO_ADDR_W + 0x801);
+}
+
+static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd,
+			     unsigned int ctrl)
+{
+	struct nand_chip *this = mtd->priv;
+	void __iomem *mmio_base = this->IO_ADDR_R;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01;
+		writeb(ctl, mmio_base + MM_NAND_CTL);
+	}
+	if (cmd != NAND_CMD_NONE)
+		cs553x_write_byte(mtd, cmd);
+}
+
+static int cs553x_device_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	void __iomem *mmio_base = this->IO_ADDR_R;
+	unsigned char foo = readb(mmio_base + MM_NAND_STS);
+
+	return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY);
+}
+
+static void cs_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	struct nand_chip *this = mtd->priv;
+	void __iomem *mmio_base = this->IO_ADDR_R;
+
+	writeb(0x07, mmio_base + MM_NAND_ECC_CTL);
+}
+
+static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+	uint32_t ecc;
+	struct nand_chip *this = mtd->priv;
+	void __iomem *mmio_base = this->IO_ADDR_R;
+
+	ecc = readl(mmio_base + MM_NAND_STS);
+
+	ecc_code[1] = ecc >> 8;
+	ecc_code[0] = ecc >> 16;
+	ecc_code[2] = ecc >> 24;
+	return 0;
+}
+
+static struct mtd_info *cs553x_mtd[4];
+
+static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
+{
+	int err = 0;
+	struct nand_chip *this;
+	struct mtd_info *new_mtd;
+
+	printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr);
+
+	if (!mmio) {
+		printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n");
+		return -ENXIO;
+	}
+
+	/* Allocate memory for MTD device structure and private data */
+	new_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
+	if (!new_mtd) {
+		printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *)(&new_mtd[1]);
+
+	/* Initialize structures */
+	memset(new_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	new_mtd->priv = this;
+	new_mtd->owner = THIS_MODULE;
+
+	/* map physical address */
+	this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
+	if (!this->IO_ADDR_R) {
+		printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr);
+		err = -EIO;
+		goto out_mtd;
+	}
+
+	this->cmd_ctrl = cs553x_hwcontrol;
+	this->dev_ready = cs553x_device_ready;
+	this->read_byte = cs553x_read_byte;
+	this->read_buf = cs553x_read_buf;
+	this->write_buf = cs553x_write_buf;
+
+	this->chip_delay = 0;
+
+	this->ecc.mode = NAND_ECC_HW;
+	this->ecc.size = 256;
+	this->ecc.bytes = 3;
+	this->ecc.hwctl  = cs_enable_hwecc;
+	this->ecc.calculate = cs_calculate_ecc;
+	this->ecc.correct  = nand_correct_data;
+
+	/* Enable the following for a flash based bad block table */
+	this->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR;
+
+	/* Scan to find existance of the device */
+	if (nand_scan(new_mtd, 1)) {
+		err = -ENXIO;
+		goto out_ior;
+	}
+
+	cs553x_mtd[cs] = new_mtd;
+	goto out;
+
+out_ior:
+	iounmap((void *)this->IO_ADDR_R);
+out_mtd:
+	kfree(new_mtd);
+out:
+	return err;
+}
+
+static int is_geode(void)
+{
+	/* These are the CPUs which will have a CS553[56] companion chip */
+	if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
+	    boot_cpu_data.x86 == 5 &&
+	    boot_cpu_data.x86_model == 10)
+		return 1; /* Geode LX */
+
+	if ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC ||
+	     boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX) &&
+	    boot_cpu_data.x86 == 5 &&
+	    boot_cpu_data.x86_model == 5)
+		return 1; /* Geode GX (née GX2) */
+
+	return 0;
+}
+
+static int __init cs553x_init(void)
+{
+	int err = -ENXIO;
+	int i;
+	uint64_t val;
+
+	/* If the CPU isn't a Geode GX or LX, abort */
+	if (!is_geode())
+		return -ENXIO;
+
+	/* If it doesn't have the CS553[56], abort */
+	rdmsrl(MSR_DIVIL_GLD_CAP, val);
+	val &= ~0xFFULL;
+	if (val != CAP_CS5535 && val != CAP_CS5536)
+		return -ENXIO;
+
+	/* If it doesn't have the NAND controller enabled, abort */
+	rdmsrl(MSR_DIVIL_BALL_OPTS, val);
+	if (val & 1) {
+		printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
+		return -ENXIO;
+	}
+
+	for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
+		rdmsrl(MSR_DIVIL_LBAR_FLSH0 + i, val);
+
+		if ((val & (FLSH_LBAR_EN|FLSH_NOR_NAND)) == (FLSH_LBAR_EN|FLSH_NOR_NAND))
+			err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF);
+	}
+
+	/* Register all devices together here. This means we can easily hack it to 
+	   do mtdconcat etc. if we want to. */
+	for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
+		if (cs553x_mtd[i]) {
+			add_mtd_device(cs553x_mtd[i]);
+
+			/* If any devices registered, return success. Else the last error. */
+			err = 0;
+		}
+	}
+
+	return err;
+}
+
+module_init(cs553x_init);
+
+static void __exit cs553x_cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
+		struct mtd_info *mtd = cs553x_mtd[i];
+		struct nand_chip *this;
+		void __iomem *mmio_base;
+
+		if (!mtd)
+			break;
+
+		this = cs553x_mtd[i]->priv;
+		mmio_base = this->IO_ADDR_R;
+
+		/* Release resources, unregister device */
+		nand_release(cs553x_mtd[i]);
+		cs553x_mtd[i] = NULL;
+
+		/* unmap physical adress */
+		iounmap(mmio_base);
+
+		/* Free the MTD device structure */
+		kfree(mtd);
+	}
+}
+
+module_exit(cs553x_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip");

+ 261 - 269
drivers/mtd/nand/diskonchip.c

@@ -58,10 +58,10 @@ static unsigned long __initdata doc_locations[] = {
 	0xe4000000,
 	0xe4000000,
 #elif defined(CONFIG_MOMENCO_OCELOT)
 #elif defined(CONFIG_MOMENCO_OCELOT)
 	0x2f000000,
 	0x2f000000,
-        0xff000000,
+	0xff000000,
 #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
 #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
-        0xff000000,
-##else
+	0xff000000,
+#else
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #endif
 #endif
 	0xffffffff };
 	0xffffffff };
@@ -73,7 +73,7 @@ struct doc_priv {
 	unsigned long physadr;
 	unsigned long physadr;
 	u_char ChipID;
 	u_char ChipID;
 	u_char CDSNControl;
 	u_char CDSNControl;
-	int chips_per_floor; /* The number of chips detected on each floor */
+	int chips_per_floor;	/* The number of chips detected on each floor */
 	int curfloor;
 	int curfloor;
 	int curchip;
 	int curchip;
 	int mh0_page;
 	int mh0_page;
@@ -84,6 +84,7 @@ struct doc_priv {
 /* This is the syndrome computed by the HW ecc generator upon reading an empty
 /* This is the syndrome computed by the HW ecc generator upon reading an empty
    page, one with all 0xff for data and stored ecc code. */
    page, one with all 0xff for data and stored ecc code. */
 static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
 static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
+
 /* This is the ecc value computed by the HW ecc generator upon writing an empty
 /* This is the ecc value computed by the HW ecc generator upon writing an empty
    page, one with all 0xff for data. */
    page, one with all 0xff for data. */
 static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
 static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
@@ -94,28 +95,29 @@ static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
 #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
 #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
 #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
 #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
 
 
-static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
+			      unsigned int bitmask);
 static void doc200x_select_chip(struct mtd_info *mtd, int chip);
 static void doc200x_select_chip(struct mtd_info *mtd, int chip);
 
 
-static int debug=0;
+static int debug = 0;
 module_param(debug, int, 0);
 module_param(debug, int, 0);
 
 
-static int try_dword=1;
+static int try_dword = 1;
 module_param(try_dword, int, 0);
 module_param(try_dword, int, 0);
 
 
-static int no_ecc_failures=0;
+static int no_ecc_failures = 0;
 module_param(no_ecc_failures, int, 0);
 module_param(no_ecc_failures, int, 0);
 
 
-static int no_autopart=0;
+static int no_autopart = 0;
 module_param(no_autopart, int, 0);
 module_param(no_autopart, int, 0);
 
 
-static int show_firmware_partition=0;
+static int show_firmware_partition = 0;
 module_param(show_firmware_partition, int, 0);
 module_param(show_firmware_partition, int, 0);
 
 
 #ifdef MTD_NAND_DISKONCHIP_BBTWRITE
 #ifdef MTD_NAND_DISKONCHIP_BBTWRITE
-static int inftl_bbt_write=1;
+static int inftl_bbt_write = 1;
 #else
 #else
-static int inftl_bbt_write=0;
+static int inftl_bbt_write = 0;
 #endif
 #endif
 module_param(inftl_bbt_write, int, 0);
 module_param(inftl_bbt_write, int, 0);
 
 
@@ -123,7 +125,6 @@ static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDR
 module_param(doc_config_location, ulong, 0);
 module_param(doc_config_location, ulong, 0);
 MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
 MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
 
 
-
 /* Sector size for HW ECC */
 /* Sector size for HW ECC */
 #define SECTOR_SIZE 512
 #define SECTOR_SIZE 512
 /* The sector bytes are packed into NB_DATA 10 bit words */
 /* The sector bytes are packed into NB_DATA 10 bit words */
@@ -147,7 +148,7 @@ static struct rs_control *rs_decoder;
  * some comments, improved a minor bit and converted it to make use
  * some comments, improved a minor bit and converted it to make use
  * of the generic Reed-Solomon libary. tglx
  * of the generic Reed-Solomon libary. tglx
  */
  */
-static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
+static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
 {
 {
 	int i, j, nerr, errpos[8];
 	int i, j, nerr, errpos[8];
 	uint8_t parity;
 	uint8_t parity;
@@ -168,18 +169,18 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
 	 *  s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]
 	 *  s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]
 	 *  where x = alpha^(FCR + i)
 	 *  where x = alpha^(FCR + i)
 	 */
 	 */
-	for(j = 1; j < NROOTS; j++) {
-		if(ds[j] == 0)
+	for (j = 1; j < NROOTS; j++) {
+		if (ds[j] == 0)
 			continue;
 			continue;
 		tmp = rs->index_of[ds[j]];
 		tmp = rs->index_of[ds[j]];
-		for(i = 0; i < NROOTS; i++)
+		for (i = 0; i < NROOTS; i++)
 			s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];
 			s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];
 	}
 	}
 
 
 	/* Calc s[i] = s[i] / alpha^(v + i) */
 	/* Calc s[i] = s[i] / alpha^(v + i) */
 	for (i = 0; i < NROOTS; i++) {
 	for (i = 0; i < NROOTS; i++) {
 		if (syn[i])
 		if (syn[i])
- 			syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i));
+			syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i));
 	}
 	}
 	/* Call the decoder library */
 	/* Call the decoder library */
 	nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);
 	nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);
@@ -193,7 +194,7 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
 	 * but they are given by the design of the de/encoder circuit
 	 * but they are given by the design of the de/encoder circuit
 	 * in the DoC ASIC's.
 	 * in the DoC ASIC's.
 	 */
 	 */
-	for(i = 0;i < nerr; i++) {
+	for (i = 0; i < nerr; i++) {
 		int index, bitpos, pos = 1015 - errpos[i];
 		int index, bitpos, pos = 1015 - errpos[i];
 		uint8_t val;
 		uint8_t val;
 		if (pos >= NB_DATA && pos < 1019)
 		if (pos >= NB_DATA && pos < 1019)
@@ -205,8 +206,7 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
 			   can be modified since pos is even */
 			   can be modified since pos is even */
 			index = (pos >> 3) ^ 1;
 			index = (pos >> 3) ^ 1;
 			bitpos = pos & 7;
 			bitpos = pos & 7;
-			if ((index >= 0 && index < SECTOR_SIZE) ||
-			    index == (SECTOR_SIZE + 1)) {
+			if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
 				val = (uint8_t) (errval[i] >> (2 + bitpos));
 				val = (uint8_t) (errval[i] >> (2 + bitpos));
 				parity ^= val;
 				parity ^= val;
 				if (index < SECTOR_SIZE)
 				if (index < SECTOR_SIZE)
@@ -216,9 +216,8 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
 			bitpos = (bitpos + 10) & 7;
 			bitpos = (bitpos + 10) & 7;
 			if (bitpos == 0)
 			if (bitpos == 0)
 				bitpos = 8;
 				bitpos = 8;
-			if ((index >= 0 && index < SECTOR_SIZE) ||
-			    index == (SECTOR_SIZE + 1)) {
-				val = (uint8_t)(errval[i] << (8 - bitpos));
+			if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
+				val = (uint8_t) (errval[i] << (8 - bitpos));
 				parity ^= val;
 				parity ^= val;
 				if (index < SECTOR_SIZE)
 				if (index < SECTOR_SIZE)
 					data[index] ^= val;
 					data[index] ^= val;
@@ -250,10 +249,11 @@ static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
 /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
 /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
 static int _DoC_WaitReady(struct doc_priv *doc)
 static int _DoC_WaitReady(struct doc_priv *doc)
 {
 {
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	unsigned long timeo = jiffies + (HZ * 10);
 	unsigned long timeo = jiffies + (HZ * 10);
 
 
-	if(debug) printk("_DoC_WaitReady...\n");
+	if (debug)
+		printk("_DoC_WaitReady...\n");
 	/* Out-of-line routine to wait for chip response */
 	/* Out-of-line routine to wait for chip response */
 	if (DoC_is_MillenniumPlus(doc)) {
 	if (DoC_is_MillenniumPlus(doc)) {
 		while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
 		while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
@@ -280,7 +280,7 @@ static int _DoC_WaitReady(struct doc_priv *doc)
 
 
 static inline int DoC_WaitReady(struct doc_priv *doc)
 static inline int DoC_WaitReady(struct doc_priv *doc)
 {
 {
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int ret = 0;
 	int ret = 0;
 
 
 	if (DoC_is_MillenniumPlus(doc)) {
 	if (DoC_is_MillenniumPlus(doc)) {
@@ -298,7 +298,8 @@ static inline int DoC_WaitReady(struct doc_priv *doc)
 		DoC_Delay(doc, 2);
 		DoC_Delay(doc, 2);
 	}
 	}
 
 
-	if(debug) printk("DoC_WaitReady OK\n");
+	if (debug)
+		printk("DoC_WaitReady OK\n");
 	return ret;
 	return ret;
 }
 }
 
 
@@ -306,9 +307,10 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 
-	if(debug)printk("write_byte %02x\n", datum);
+	if (debug)
+		printk("write_byte %02x\n", datum);
 	WriteDOC(datum, docptr, CDSNSlowIO);
 	WriteDOC(datum, docptr, CDSNSlowIO);
 	WriteDOC(datum, docptr, 2k_CDSN_IO);
 	WriteDOC(datum, docptr, 2k_CDSN_IO);
 }
 }
@@ -317,77 +319,78 @@ static u_char doc2000_read_byte(struct mtd_info *mtd)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	u_char ret;
 	u_char ret;
 
 
 	ReadDOC(docptr, CDSNSlowIO);
 	ReadDOC(docptr, CDSNSlowIO);
 	DoC_Delay(doc, 2);
 	DoC_Delay(doc, 2);
 	ret = ReadDOC(docptr, 2k_CDSN_IO);
 	ret = ReadDOC(docptr, 2k_CDSN_IO);
-	if (debug) printk("read_byte returns %02x\n", ret);
+	if (debug)
+		printk("read_byte returns %02x\n", ret);
 	return ret;
 	return ret;
 }
 }
 
 
-static void doc2000_writebuf(struct mtd_info *mtd,
-			     const u_char *buf, int len)
+static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
-	if (debug)printk("writebuf of %d bytes: ", len);
-	for (i=0; i < len; i++) {
+	if (debug)
+		printk("writebuf of %d bytes: ", len);
+	for (i = 0; i < len; i++) {
 		WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
 		WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
 		if (debug && i < 16)
 		if (debug && i < 16)
 			printk("%02x ", buf[i]);
 			printk("%02x ", buf[i]);
 	}
 	}
-	if (debug) printk("\n");
+	if (debug)
+		printk("\n");
 }
 }
 
 
-static void doc2000_readbuf(struct mtd_info *mtd,
-			    u_char *buf, int len)
+static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
- 	int i;
+	void __iomem *docptr = doc->virtadr;
+	int i;
 
 
-	if (debug)printk("readbuf of %d bytes: ", len);
+	if (debug)
+		printk("readbuf of %d bytes: ", len);
 
 
-	for (i=0; i < len; i++) {
+	for (i = 0; i < len; i++) {
 		buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
 		buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
 	}
 	}
 }
 }
 
 
-static void doc2000_readbuf_dword(struct mtd_info *mtd,
-			    u_char *buf, int len)
+static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
- 	int i;
+	void __iomem *docptr = doc->virtadr;
+	int i;
 
 
-	if (debug) printk("readbuf_dword of %d bytes: ", len);
+	if (debug)
+		printk("readbuf_dword of %d bytes: ", len);
 
 
-	if (unlikely((((unsigned long)buf)|len) & 3)) {
-		for (i=0; i < len; i++) {
-			*(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
+	if (unlikely((((unsigned long)buf) | len) & 3)) {
+		for (i = 0; i < len; i++) {
+			*(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
 		}
 		}
 	} else {
 	} else {
-		for (i=0; i < len; i+=4) {
-			*(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
+		for (i = 0; i < len; i += 4) {
+			*(uint32_t *) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
 		}
 		}
 	}
 	}
 }
 }
 
 
-static int doc2000_verifybuf(struct mtd_info *mtd,
-			      const u_char *buf, int len)
+static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
 
 
-	for (i=0; i < len; i++)
+	for (i = 0; i < len; i++)
 		if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
 		if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
 			return -EFAULT;
 			return -EFAULT;
 	return 0;
 	return 0;
@@ -400,12 +403,10 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
 	uint16_t ret;
 	uint16_t ret;
 
 
 	doc200x_select_chip(mtd, nr);
 	doc200x_select_chip(mtd, nr);
-	doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
-	this->write_byte(mtd, NAND_CMD_READID);
-	doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
-	doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
-	this->write_byte(mtd, 0);
-	doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+	doc200x_hwcontrol(mtd, NAND_CMD_READID,
+			  NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+	doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+	doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 
 
 	/* We cant' use dev_ready here, but at least we wait for the
 	/* We cant' use dev_ready here, but at least we wait for the
 	 * command to complete
 	 * command to complete
@@ -423,12 +424,11 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
 		} ident;
 		} ident;
 		void __iomem *docptr = doc->virtadr;
 		void __iomem *docptr = doc->virtadr;
 
 
-		doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
-		doc2000_write_byte(mtd, NAND_CMD_READID);
-		doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
-		doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
-		doc2000_write_byte(mtd, 0);
-		doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+		doc200x_hwcontrol(mtd, NAND_CMD_READID,
+				  NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+		doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+		doc200x_hwcontrol(mtd, NAND_CMD_NONE,
+				  NAND_NCE | NAND_CTRL_CHANGE);
 
 
 		udelay(50);
 		udelay(50);
 
 
@@ -464,7 +464,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd)
 	printk(KERN_DEBUG "Detected %d chips per floor.\n", i);
 	printk(KERN_DEBUG "Detected %d chips per floor.\n", i);
 }
 }
 
 
-static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)
 {
 {
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
 
 
@@ -482,7 +482,7 @@ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 
 	WriteDOC(datum, docptr, CDSNSlowIO);
 	WriteDOC(datum, docptr, CDSNSlowIO);
 	WriteDOC(datum, docptr, Mil_CDSN_IO);
 	WriteDOC(datum, docptr, Mil_CDSN_IO);
@@ -493,7 +493,7 @@ static u_char doc2001_read_byte(struct mtd_info *mtd)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 
 	//ReadDOC(docptr, CDSNSlowIO);
 	//ReadDOC(docptr, CDSNSlowIO);
 	/* 11.4.5 -- delay twice to allow extended length cycle */
 	/* 11.4.5 -- delay twice to allow extended length cycle */
@@ -503,50 +503,47 @@ static u_char doc2001_read_byte(struct mtd_info *mtd)
 	return ReadDOC(docptr, LastDataRead);
 	return ReadDOC(docptr, LastDataRead);
 }
 }
 
 
-static void doc2001_writebuf(struct mtd_info *mtd,
-			     const u_char *buf, int len)
+static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
 
 
-	for (i=0; i < len; i++)
+	for (i = 0; i < len; i++)
 		WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
 		WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
 	/* Terminate write pipeline */
 	/* Terminate write pipeline */
 	WriteDOC(0x00, docptr, WritePipeTerm);
 	WriteDOC(0x00, docptr, WritePipeTerm);
 }
 }
 
 
-static void doc2001_readbuf(struct mtd_info *mtd,
-			    u_char *buf, int len)
+static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
 
 
 	/* Start read pipeline */
 	/* Start read pipeline */
 	ReadDOC(docptr, ReadPipeInit);
 	ReadDOC(docptr, ReadPipeInit);
 
 
-	for (i=0; i < len-1; i++)
+	for (i = 0; i < len - 1; i++)
 		buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
 		buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
 
 
 	/* Terminate read pipeline */
 	/* Terminate read pipeline */
 	buf[i] = ReadDOC(docptr, LastDataRead);
 	buf[i] = ReadDOC(docptr, LastDataRead);
 }
 }
 
 
-static int doc2001_verifybuf(struct mtd_info *mtd,
-			     const u_char *buf, int len)
+static int doc2001_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
 
 
 	/* Start read pipeline */
 	/* Start read pipeline */
 	ReadDOC(docptr, ReadPipeInit);
 	ReadDOC(docptr, ReadPipeInit);
 
 
-	for (i=0; i < len-1; i++)
+	for (i = 0; i < len - 1; i++)
 		if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
 		if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
 			ReadDOC(docptr, LastDataRead);
 			ReadDOC(docptr, LastDataRead);
 			return i;
 			return i;
@@ -560,87 +557,90 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	u_char ret;
 	u_char ret;
 
 
-        ReadDOC(docptr, Mplus_ReadPipeInit);
-        ReadDOC(docptr, Mplus_ReadPipeInit);
-        ret = ReadDOC(docptr, Mplus_LastDataRead);
-	if (debug) printk("read_byte returns %02x\n", ret);
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+	ret = ReadDOC(docptr, Mplus_LastDataRead);
+	if (debug)
+		printk("read_byte returns %02x\n", ret);
 	return ret;
 	return ret;
 }
 }
 
 
-static void doc2001plus_writebuf(struct mtd_info *mtd,
-			     const u_char *buf, int len)
+static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
 
 
-	if (debug)printk("writebuf of %d bytes: ", len);
-	for (i=0; i < len; i++) {
+	if (debug)
+		printk("writebuf of %d bytes: ", len);
+	for (i = 0; i < len; i++) {
 		WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
 		WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
 		if (debug && i < 16)
 		if (debug && i < 16)
 			printk("%02x ", buf[i]);
 			printk("%02x ", buf[i]);
 	}
 	}
-	if (debug) printk("\n");
+	if (debug)
+		printk("\n");
 }
 }
 
 
-static void doc2001plus_readbuf(struct mtd_info *mtd,
-			    u_char *buf, int len)
+static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
 
 
-	if (debug)printk("readbuf of %d bytes: ", len);
+	if (debug)
+		printk("readbuf of %d bytes: ", len);
 
 
 	/* Start read pipeline */
 	/* Start read pipeline */
 	ReadDOC(docptr, Mplus_ReadPipeInit);
 	ReadDOC(docptr, Mplus_ReadPipeInit);
 	ReadDOC(docptr, Mplus_ReadPipeInit);
 	ReadDOC(docptr, Mplus_ReadPipeInit);
 
 
-	for (i=0; i < len-2; i++) {
+	for (i = 0; i < len - 2; i++) {
 		buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
 		buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
 		if (debug && i < 16)
 		if (debug && i < 16)
 			printk("%02x ", buf[i]);
 			printk("%02x ", buf[i]);
 	}
 	}
 
 
 	/* Terminate read pipeline */
 	/* Terminate read pipeline */
-	buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead);
+	buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead);
 	if (debug && i < 16)
 	if (debug && i < 16)
-		printk("%02x ", buf[len-2]);
-	buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead);
+		printk("%02x ", buf[len - 2]);
+	buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead);
 	if (debug && i < 16)
 	if (debug && i < 16)
-		printk("%02x ", buf[len-1]);
-	if (debug) printk("\n");
+		printk("%02x ", buf[len - 1]);
+	if (debug)
+		printk("\n");
 }
 }
 
 
-static int doc2001plus_verifybuf(struct mtd_info *mtd,
-			     const u_char *buf, int len)
+static int doc2001plus_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
 
 
-	if (debug)printk("verifybuf of %d bytes: ", len);
+	if (debug)
+		printk("verifybuf of %d bytes: ", len);
 
 
 	/* Start read pipeline */
 	/* Start read pipeline */
 	ReadDOC(docptr, Mplus_ReadPipeInit);
 	ReadDOC(docptr, Mplus_ReadPipeInit);
 	ReadDOC(docptr, Mplus_ReadPipeInit);
 	ReadDOC(docptr, Mplus_ReadPipeInit);
 
 
-	for (i=0; i < len-2; i++)
+	for (i = 0; i < len - 2; i++)
 		if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
 		if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
 			ReadDOC(docptr, Mplus_LastDataRead);
 			ReadDOC(docptr, Mplus_LastDataRead);
 			ReadDOC(docptr, Mplus_LastDataRead);
 			ReadDOC(docptr, Mplus_LastDataRead);
 			return i;
 			return i;
 		}
 		}
-	if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead))
-		return len-2;
-	if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead))
-		return len-1;
+	if (buf[len - 2] != ReadDOC(docptr, Mplus_LastDataRead))
+		return len - 2;
+	if (buf[len - 1] != ReadDOC(docptr, Mplus_LastDataRead))
+		return len - 1;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -648,10 +648,11 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int floor = 0;
 	int floor = 0;
 
 
-	if(debug)printk("select chip (%d)\n", chip);
+	if (debug)
+		printk("select chip (%d)\n", chip);
 
 
 	if (chip == -1) {
 	if (chip == -1) {
 		/* Disable flash internally */
 		/* Disable flash internally */
@@ -660,7 +661,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
 	}
 	}
 
 
 	floor = chip / doc->chips_per_floor;
 	floor = chip / doc->chips_per_floor;
-	chip -= (floor *  doc->chips_per_floor);
+	chip -= (floor * doc->chips_per_floor);
 
 
 	/* Assert ChipEnable and deassert WriteProtect */
 	/* Assert ChipEnable and deassert WriteProtect */
 	WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect);
 	WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect);
@@ -674,72 +675,61 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int floor = 0;
 	int floor = 0;
 
 
-	if(debug)printk("select chip (%d)\n", chip);
+	if (debug)
+		printk("select chip (%d)\n", chip);
 
 
 	if (chip == -1)
 	if (chip == -1)
 		return;
 		return;
 
 
 	floor = chip / doc->chips_per_floor;
 	floor = chip / doc->chips_per_floor;
-	chip -= (floor *  doc->chips_per_floor);
+	chip -= (floor * doc->chips_per_floor);
 
 
 	/* 11.4.4 -- deassert CE before changing chip */
 	/* 11.4.4 -- deassert CE before changing chip */
-	doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
+	doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
 
 
 	WriteDOC(floor, docptr, FloorSelect);
 	WriteDOC(floor, docptr, FloorSelect);
 	WriteDOC(chip, docptr, CDSNDeviceSelect);
 	WriteDOC(chip, docptr, CDSNDeviceSelect);
 
 
-	doc200x_hwcontrol(mtd, NAND_CTL_SETNCE);
+	doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 
 
 	doc->curchip = chip;
 	doc->curchip = chip;
 	doc->curfloor = floor;
 	doc->curfloor = floor;
 }
 }
 
 
-static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
+#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE)
+
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
+			      unsigned int ctrl)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 
-	switch(cmd) {
-	case NAND_CTL_SETNCE:
-		doc->CDSNControl |= CDSN_CTRL_CE;
-		break;
-	case NAND_CTL_CLRNCE:
-		doc->CDSNControl &= ~CDSN_CTRL_CE;
-		break;
-	case NAND_CTL_SETCLE:
-		doc->CDSNControl |= CDSN_CTRL_CLE;
-		break;
-	case NAND_CTL_CLRCLE:
-		doc->CDSNControl &= ~CDSN_CTRL_CLE;
-		break;
-	case NAND_CTL_SETALE:
-		doc->CDSNControl |= CDSN_CTRL_ALE;
-		break;
-	case NAND_CTL_CLRALE:
-		doc->CDSNControl &= ~CDSN_CTRL_ALE;
-		break;
-	case NAND_CTL_SETWP:
-		doc->CDSNControl |= CDSN_CTRL_WP;
-		break;
-	case NAND_CTL_CLRWP:
-		doc->CDSNControl &= ~CDSN_CTRL_WP;
-		break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		doc->CDSNControl &= ~CDSN_CTRL_MSK;
+		doc->CDSNControl |= ctrl & CDSN_CTRL_MSK;
+		if (debug)
+			printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
+		WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+		/* 11.4.3 -- 4 NOPs after CSDNControl write */
+		DoC_Delay(doc, 4);
+	}
+	if (cmd != NAND_CMD_NONE) {
+		if (DoC_is_2000(doc))
+			doc2000_write_byte(mtd, cmd);
+		else
+			doc2001_write_byte(mtd, cmd);
 	}
 	}
-	if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
-	WriteDOC(doc->CDSNControl, docptr, CDSNControl);
-	/* 11.4.3 -- 4 NOPs after CSDNControl write */
-	DoC_Delay(doc, 4);
 }
 }
 
 
-static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 
 	/*
 	/*
 	 * Must terminate write pipeline before sending any commands
 	 * Must terminate write pipeline before sending any commands
@@ -756,9 +746,9 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
 	if (command == NAND_CMD_SEQIN) {
 	if (command == NAND_CMD_SEQIN) {
 		int readcmd;
 		int readcmd;
 
 
-		if (column >= mtd->oobblock) {
+		if (column >= mtd->writesize) {
 			/* OOB area */
 			/* OOB area */
-			column -= mtd->oobblock;
+			column -= mtd->writesize;
 			readcmd = NAND_CMD_READOOB;
 			readcmd = NAND_CMD_READOOB;
 		} else if (column < 256) {
 		} else if (column < 256) {
 			/* First 256 bytes --> READ0 */
 			/* First 256 bytes --> READ0 */
@@ -782,25 +772,26 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
 			WriteDOC(column, docptr, Mplus_FlashAddress);
 			WriteDOC(column, docptr, Mplus_FlashAddress);
 		}
 		}
 		if (page_addr != -1) {
 		if (page_addr != -1) {
-			WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress);
-			WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress);
+			WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress);
+			WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress);
 			/* One more address cycle for higher density devices */
 			/* One more address cycle for higher density devices */
 			if (this->chipsize & 0x0c000000) {
 			if (this->chipsize & 0x0c000000) {
-				WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress);
+				WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress);
 				printk("high density\n");
 				printk("high density\n");
 			}
 			}
 		}
 		}
 		WriteDOC(0, docptr, Mplus_WritePipeTerm);
 		WriteDOC(0, docptr, Mplus_WritePipeTerm);
 		WriteDOC(0, docptr, Mplus_WritePipeTerm);
 		WriteDOC(0, docptr, Mplus_WritePipeTerm);
 		/* deassert ALE */
 		/* deassert ALE */
-		if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID)
+		if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
+		    command == NAND_CMD_READOOB || command == NAND_CMD_READID)
 			WriteDOC(0, docptr, Mplus_FlashControl);
 			WriteDOC(0, docptr, Mplus_FlashControl);
 	}
 	}
 
 
 	/*
 	/*
 	 * program and erase have their own busy handlers
 	 * program and erase have their own busy handlers
 	 * status and sequential in needs no delay
 	 * status and sequential in needs no delay
-	*/
+	 */
 	switch (command) {
 	switch (command) {
 
 
 	case NAND_CMD_PAGEPROG:
 	case NAND_CMD_PAGEPROG:
@@ -817,55 +808,57 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
 		WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd);
 		WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd);
 		WriteDOC(0, docptr, Mplus_WritePipeTerm);
 		WriteDOC(0, docptr, Mplus_WritePipeTerm);
 		WriteDOC(0, docptr, Mplus_WritePipeTerm);
 		WriteDOC(0, docptr, Mplus_WritePipeTerm);
-		while ( !(this->read_byte(mtd) & 0x40));
+		while (!(this->read_byte(mtd) & 0x40)) ;
 		return;
 		return;
 
 
-	/* This applies to read commands */
+		/* This applies to read commands */
 	default:
 	default:
 		/*
 		/*
 		 * If we don't have access to the busy pin, we apply the given
 		 * If we don't have access to the busy pin, we apply the given
 		 * command delay
 		 * command delay
-		*/
+		 */
 		if (!this->dev_ready) {
 		if (!this->dev_ready) {
-			udelay (this->chip_delay);
+			udelay(this->chip_delay);
 			return;
 			return;
 		}
 		}
 	}
 	}
 
 
 	/* Apply this short delay always to ensure that we do wait tWB in
 	/* Apply this short delay always to ensure that we do wait tWB in
 	 * any case on any machine. */
 	 * any case on any machine. */
-	ndelay (100);
+	ndelay(100);
 	/* wait until command is processed */
 	/* wait until command is processed */
-	while (!this->dev_ready(mtd));
+	while (!this->dev_ready(mtd)) ;
 }
 }
 
 
 static int doc200x_dev_ready(struct mtd_info *mtd)
 static int doc200x_dev_ready(struct mtd_info *mtd)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 
 	if (DoC_is_MillenniumPlus(doc)) {
 	if (DoC_is_MillenniumPlus(doc)) {
 		/* 11.4.2 -- must NOP four times before checking FR/B# */
 		/* 11.4.2 -- must NOP four times before checking FR/B# */
 		DoC_Delay(doc, 4);
 		DoC_Delay(doc, 4);
 		if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
 		if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
-			if(debug)
+			if (debug)
 				printk("not ready\n");
 				printk("not ready\n");
 			return 0;
 			return 0;
 		}
 		}
-		if (debug)printk("was ready\n");
+		if (debug)
+			printk("was ready\n");
 		return 1;
 		return 1;
 	} else {
 	} else {
 		/* 11.4.2 -- must NOP four times before checking FR/B# */
 		/* 11.4.2 -- must NOP four times before checking FR/B# */
 		DoC_Delay(doc, 4);
 		DoC_Delay(doc, 4);
 		if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
 		if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
-			if(debug)
+			if (debug)
 				printk("not ready\n");
 				printk("not ready\n");
 			return 0;
 			return 0;
 		}
 		}
 		/* 11.4.2 -- Must NOP twice if it's ready */
 		/* 11.4.2 -- Must NOP twice if it's ready */
 		DoC_Delay(doc, 2);
 		DoC_Delay(doc, 2);
-		if (debug)printk("was ready\n");
+		if (debug)
+			printk("was ready\n");
 		return 1;
 		return 1;
 	}
 	}
 }
 }
@@ -881,10 +874,10 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 
 	/* Prime the ECC engine */
 	/* Prime the ECC engine */
-	switch(mode) {
+	switch (mode) {
 	case NAND_ECC_READ:
 	case NAND_ECC_READ:
 		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
 		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
 		WriteDOC(DOC_ECC_EN, docptr, ECCConf);
 		WriteDOC(DOC_ECC_EN, docptr, ECCConf);
@@ -900,10 +893,10 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 
 	/* Prime the ECC engine */
 	/* Prime the ECC engine */
-	switch(mode) {
+	switch (mode) {
 	case NAND_ECC_READ:
 	case NAND_ECC_READ:
 		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
 		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
 		WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
 		WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
@@ -916,12 +909,11 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
 }
 }
 
 
 /* This code is only called on write */
 /* This code is only called on write */
-static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
-				 unsigned char *ecc_code)
+static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 	int i;
 	int emptymatch = 1;
 	int emptymatch = 1;
 
 
@@ -961,7 +953,8 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 		   often.  It could be optimized away by examining the data in
 		   often.  It could be optimized away by examining the data in
 		   the writebuf routine, and remembering the result. */
 		   the writebuf routine, and remembering the result. */
 		for (i = 0; i < 512; i++) {
 		for (i = 0; i < 512; i++) {
-			if (dat[i] == 0xff) continue;
+			if (dat[i] == 0xff)
+				continue;
 			emptymatch = 0;
 			emptymatch = 0;
 			break;
 			break;
 		}
 		}
@@ -969,17 +962,20 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 	/* If emptymatch still =1, we do have an all-0xff data buffer.
 	/* If emptymatch still =1, we do have an all-0xff data buffer.
 	   Return all-0xff ecc value instead of the computed one, so
 	   Return all-0xff ecc value instead of the computed one, so
 	   it'll look just like a freshly-erased page. */
 	   it'll look just like a freshly-erased page. */
-	if (emptymatch) memset(ecc_code, 0xff, 6);
+	if (emptymatch)
+		memset(ecc_code, 0xff, 6);
 #endif
 #endif
 	return 0;
 	return 0;
 }
 }
 
 
-static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
+				u_char *read_ecc, u_char *isnull)
 {
 {
 	int i, ret = 0;
 	int i, ret = 0;
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
-        void __iomem *docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
+	uint8_t calc_ecc[6];
 	volatile u_char dummy;
 	volatile u_char dummy;
 	int emptymatch = 1;
 	int emptymatch = 1;
 
 
@@ -1012,18 +1008,20 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
 		   all-0xff data and stored ecc block.  Check the stored ecc. */
 		   all-0xff data and stored ecc block.  Check the stored ecc. */
 		if (emptymatch) {
 		if (emptymatch) {
 			for (i = 0; i < 6; i++) {
 			for (i = 0; i < 6; i++) {
-				if (read_ecc[i] == 0xff) continue;
+				if (read_ecc[i] == 0xff)
+					continue;
 				emptymatch = 0;
 				emptymatch = 0;
 				break;
 				break;
 			}
 			}
 		}
 		}
 		/* If emptymatch still =1, check the data block. */
 		/* If emptymatch still =1, check the data block. */
 		if (emptymatch) {
 		if (emptymatch) {
-		/* Note: this somewhat expensive test should not be triggered
-		   often.  It could be optimized away by examining the data in
-		   the readbuf routine, and remembering the result. */
+			/* Note: this somewhat expensive test should not be triggered
+			   often.  It could be optimized away by examining the data in
+			   the readbuf routine, and remembering the result. */
 			for (i = 0; i < 512; i++) {
 			for (i = 0; i < 512; i++) {
-				if (dat[i] == 0xff) continue;
+				if (dat[i] == 0xff)
+					continue;
 				emptymatch = 0;
 				emptymatch = 0;
 				break;
 				break;
 			}
 			}
@@ -1032,7 +1030,8 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
 		   erased block, in which case the ECC will not come out right.
 		   erased block, in which case the ECC will not come out right.
 		   We'll suppress the error and tell the caller everything's
 		   We'll suppress the error and tell the caller everything's
 		   OK.  Because it is. */
 		   OK.  Because it is. */
-		if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc);
+		if (!emptymatch)
+			ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);
 		if (ret > 0)
 		if (ret > 0)
 			printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
 			printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
 	}
 	}
@@ -1059,11 +1058,10 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
  * safer.  The only problem with it is that any code that parses oobfree must
  * safer.  The only problem with it is that any code that parses oobfree must
  * be able to handle out-of-order segments.
  * be able to handle out-of-order segments.
  */
  */
-static struct nand_oobinfo doc200x_oobinfo = {
-        .useecc = MTD_NANDECC_AUTOPLACE,
-        .eccbytes = 6,
-        .eccpos = {0, 1, 2, 3, 4, 5},
-        .oobfree = { {8, 8}, {6, 2} }
+static struct nand_ecclayout doc200x_oobinfo = {
+	.eccbytes = 6,
+	.eccpos = {0, 1, 2, 3, 4, 5},
+	.oobfree = {{8, 8}, {6, 2}}
 };
 };
 
 
 /* Find the (I)NFTL Media Header, and optionally also the mirror media header.
 /* Find the (I)NFTL Media Header, and optionally also the mirror media header.
@@ -1072,8 +1070,7 @@ static struct nand_oobinfo doc200x_oobinfo = {
    either "ANAND" or "BNAND".  If findmirror=1, also look for the mirror media
    either "ANAND" or "BNAND".  If findmirror=1, also look for the mirror media
    header.  The page #s of the found media headers are placed in mh0_page and
    header.  The page #s of the found media headers are placed in mh0_page and
    mh1_page in the DOC private structure. */
    mh1_page in the DOC private structure. */
-static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
-				     const char *id, int findmirror)
+static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
@@ -1082,17 +1079,19 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
 	size_t retlen;
 	size_t retlen;
 
 
 	for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
 	for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
-		ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
-		if (retlen != mtd->oobblock) continue;
+		ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf);
+		if (retlen != mtd->writesize)
+			continue;
 		if (ret) {
 		if (ret) {
-			printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n",
-				offs);
+			printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs);
 		}
 		}
-		if (memcmp(buf, id, 6)) continue;
+		if (memcmp(buf, id, 6))
+			continue;
 		printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
 		printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
 		if (doc->mh0_page == -1) {
 		if (doc->mh0_page == -1) {
 			doc->mh0_page = offs >> this->page_shift;
 			doc->mh0_page = offs >> this->page_shift;
-			if (!findmirror) return 1;
+			if (!findmirror)
+				return 1;
 			continue;
 			continue;
 		}
 		}
 		doc->mh1_page = offs >> this->page_shift;
 		doc->mh1_page = offs >> this->page_shift;
@@ -1105,8 +1104,8 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
 	/* Only one mediaheader was found.  We want buf to contain a
 	/* Only one mediaheader was found.  We want buf to contain a
 	   mediaheader on return, so we'll have to re-read the one we found. */
 	   mediaheader on return, so we'll have to re-read the one we found. */
 	offs = doc->mh0_page << this->page_shift;
 	offs = doc->mh0_page << this->page_shift;
-	ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
-	if (retlen != mtd->oobblock) {
+	ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf);
+	if (retlen != mtd->writesize) {
 		/* Insanity.  Give up. */
 		/* Insanity.  Give up. */
 		printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
 		printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
 		return 0;
 		return 0;
@@ -1114,8 +1113,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
 	return 1;
 	return 1;
 }
 }
 
 
-static inline int __init nftl_partscan(struct mtd_info *mtd,
-				struct mtd_partition *parts)
+static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
@@ -1127,13 +1125,14 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
 	unsigned blocks, maxblocks;
 	unsigned blocks, maxblocks;
 	int offs, numheaders;
 	int offs, numheaders;
 
 
-	buf = kmalloc(mtd->oobblock, GFP_KERNEL);
+	buf = kmalloc(mtd->writesize, GFP_KERNEL);
 	if (!buf) {
 	if (!buf) {
 		printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
 		printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
 		return 0;
 		return 0;
 	}
 	}
-	if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out;
-	mh = (struct NFTLMediaHeader *) buf;
+	if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1)))
+		goto out;
+	mh = (struct NFTLMediaHeader *)buf;
 
 
 	mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits);
 	mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits);
 	mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN);
 	mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN);
@@ -1155,8 +1154,8 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
 		/* Auto-determine UnitSizeFactor.  The constraints are:
 		/* Auto-determine UnitSizeFactor.  The constraints are:
 		   - There can be at most 32768 virtual blocks.
 		   - There can be at most 32768 virtual blocks.
 		   - There can be at most (virtual block size - page size)
 		   - There can be at most (virtual block size - page size)
-		     virtual blocks (because MediaHeader+BBT must fit in 1).
-		*/
+		   virtual blocks (because MediaHeader+BBT must fit in 1).
+		 */
 		mh->UnitSizeFactor = 0xff;
 		mh->UnitSizeFactor = 0xff;
 		while (blocks > maxblocks) {
 		while (blocks > maxblocks) {
 			blocks >>= 1;
 			blocks >>= 1;
@@ -1211,14 +1210,13 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
 	}
 	}
 
 
 	ret = numparts;
 	ret = numparts;
-out:
+ out:
 	kfree(buf);
 	kfree(buf);
 	return ret;
 	return ret;
 }
 }
 
 
 /* This is a stripped-down copy of the code in inftlmount.c */
 /* This is a stripped-down copy of the code in inftlmount.c */
-static inline int __init inftl_partscan(struct mtd_info *mtd,
-				 struct mtd_partition *parts)
+static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
@@ -1235,15 +1233,16 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
 	if (inftl_bbt_write)
 	if (inftl_bbt_write)
 		end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
 		end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
 
 
-	buf = kmalloc(mtd->oobblock, GFP_KERNEL);
+	buf = kmalloc(mtd->writesize, GFP_KERNEL);
 	if (!buf) {
 	if (!buf) {
 		printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
 		printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
 		return 0;
 		return 0;
 	}
 	}
 
 
-	if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out;
+	if (!find_media_headers(mtd, buf, "BNAND", 0))
+		goto out;
 	doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
 	doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
-	mh = (struct INFTLMediaHeader *) buf;
+	mh = (struct INFTLMediaHeader *)buf;
 
 
 	mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
 	mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
 	mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
 	mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
@@ -1319,8 +1318,10 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
 		parts[numparts].offset = ip->firstUnit << vshift;
 		parts[numparts].offset = ip->firstUnit << vshift;
 		parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
 		parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
 		numparts++;
 		numparts++;
-		if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit;
-		if (ip->flags & INFTL_LAST) break;
+		if (ip->lastUnit > lastvunit)
+			lastvunit = ip->lastUnit;
+		if (ip->flags & INFTL_LAST)
+			break;
 	}
 	}
 	lastvunit++;
 	lastvunit++;
 	if ((lastvunit << vshift) < end) {
 	if ((lastvunit << vshift) < end) {
@@ -1330,7 +1331,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
 		numparts++;
 		numparts++;
 	}
 	}
 	ret = numparts;
 	ret = numparts;
-out:
+ out:
 	kfree(buf);
 	kfree(buf);
 	return ret;
 	return ret;
 }
 }
@@ -1342,11 +1343,12 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
 	struct mtd_partition parts[2];
 	struct mtd_partition parts[2];
 
 
-	memset((char *) parts, 0, sizeof(parts));
+	memset((char *)parts, 0, sizeof(parts));
 	/* On NFTL, we have to find the media headers before we can read the
 	/* On NFTL, we have to find the media headers before we can read the
 	   BBTs, since they're stored in the media header eraseblocks. */
 	   BBTs, since they're stored in the media header eraseblocks. */
 	numparts = nftl_partscan(mtd, parts);
 	numparts = nftl_partscan(mtd, parts);
-	if (!numparts) return -EIO;
+	if (!numparts)
+		return -EIO;
 	this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
 	this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
 				NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
 				NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
 				NAND_BBT_VERSION;
 				NAND_BBT_VERSION;
@@ -1393,8 +1395,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
 		this->bbt_td->pages[0] = 2;
 		this->bbt_td->pages[0] = 2;
 		this->bbt_md = NULL;
 		this->bbt_md = NULL;
 	} else {
 	} else {
-		this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
-					NAND_BBT_VERSION;
+		this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;
 		if (inftl_bbt_write)
 		if (inftl_bbt_write)
 			this->bbt_td->options |= NAND_BBT_WRITE;
 			this->bbt_td->options |= NAND_BBT_WRITE;
 		this->bbt_td->offs = 8;
 		this->bbt_td->offs = 8;
@@ -1404,8 +1405,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
 		this->bbt_td->reserved_block_code = 0x01;
 		this->bbt_td->reserved_block_code = 0x01;
 		this->bbt_td->pattern = "MSYS_BBT";
 		this->bbt_td->pattern = "MSYS_BBT";
 
 
-		this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
-					NAND_BBT_VERSION;
+		this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;
 		if (inftl_bbt_write)
 		if (inftl_bbt_write)
 			this->bbt_md->options |= NAND_BBT_WRITE;
 			this->bbt_md->options |= NAND_BBT_WRITE;
 		this->bbt_md->offs = 8;
 		this->bbt_md->offs = 8;
@@ -1420,12 +1420,13 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
 	   At least as nand_bbt.c is currently written. */
 	   At least as nand_bbt.c is currently written. */
 	if ((ret = nand_scan_bbt(mtd, NULL)))
 	if ((ret = nand_scan_bbt(mtd, NULL)))
 		return ret;
 		return ret;
-	memset((char *) parts, 0, sizeof(parts));
+	memset((char *)parts, 0, sizeof(parts));
 	numparts = inftl_partscan(mtd, parts);
 	numparts = inftl_partscan(mtd, parts);
 	/* At least for now, require the INFTL Media Header.  We could probably
 	/* At least for now, require the INFTL Media Header.  We could probably
 	   do without it for non-INFTL use, since all it gives us is
 	   do without it for non-INFTL use, since all it gives us is
 	   autopartitioning, but I want to give it more thought. */
 	   autopartitioning, but I want to give it more thought. */
-	if (!numparts) return -EIO;
+	if (!numparts)
+		return -EIO;
 	add_mtd_device(mtd);
 	add_mtd_device(mtd);
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
 	if (!no_autopart)
 	if (!no_autopart)
@@ -1439,7 +1440,6 @@ static inline int __init doc2000_init(struct mtd_info *mtd)
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
 
 
-	this->write_byte = doc2000_write_byte;
 	this->read_byte = doc2000_read_byte;
 	this->read_byte = doc2000_read_byte;
 	this->write_buf = doc2000_writebuf;
 	this->write_buf = doc2000_writebuf;
 	this->read_buf = doc2000_readbuf;
 	this->read_buf = doc2000_readbuf;
@@ -1457,7 +1457,6 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
 
 
-	this->write_byte = doc2001_write_byte;
 	this->read_byte = doc2001_read_byte;
 	this->read_byte = doc2001_read_byte;
 	this->write_buf = doc2001_writebuf;
 	this->write_buf = doc2001_writebuf;
 	this->read_buf = doc2001_readbuf;
 	this->read_buf = doc2001_readbuf;
@@ -1489,16 +1488,15 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	struct doc_priv *doc = this->priv;
 
 
-	this->write_byte = NULL;
 	this->read_byte = doc2001plus_read_byte;
 	this->read_byte = doc2001plus_read_byte;
 	this->write_buf = doc2001plus_writebuf;
 	this->write_buf = doc2001plus_writebuf;
 	this->read_buf = doc2001plus_readbuf;
 	this->read_buf = doc2001plus_readbuf;
 	this->verify_buf = doc2001plus_verifybuf;
 	this->verify_buf = doc2001plus_verifybuf;
 	this->scan_bbt = inftl_scan_bbt;
 	this->scan_bbt = inftl_scan_bbt;
-	this->hwcontrol = NULL;
+	this->cmd_ctrl = NULL;
 	this->select_chip = doc2001plus_select_chip;
 	this->select_chip = doc2001plus_select_chip;
 	this->cmdfunc = doc2001plus_command;
 	this->cmdfunc = doc2001plus_command;
-	this->enable_hwecc = doc2001plus_enable_hwecc;
+	this->ecc.hwctl = doc2001plus_enable_hwecc;
 
 
 	doc->chips_per_floor = 1;
 	doc->chips_per_floor = 1;
 	mtd->name = "DiskOnChip Millennium Plus";
 	mtd->name = "DiskOnChip Millennium Plus";
@@ -1535,20 +1533,16 @@ static int __init doc_probe(unsigned long physadr)
 	save_control = ReadDOC(virtadr, DOCControl);
 	save_control = ReadDOC(virtadr, DOCControl);
 
 
 	/* Reset the DiskOnChip ASIC */
 	/* Reset the DiskOnChip ASIC */
-	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
-		 virtadr, DOCControl);
-	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
-		 virtadr, DOCControl);
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);
 
 
 	/* Enable the DiskOnChip ASIC */
 	/* Enable the DiskOnChip ASIC */
-	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
-		 virtadr, DOCControl);
-	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
-		 virtadr, DOCControl);
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);
 
 
 	ChipID = ReadDOC(virtadr, ChipID);
 	ChipID = ReadDOC(virtadr, ChipID);
 
 
-	switch(ChipID) {
+	switch (ChipID) {
 	case DOC_ChipID_Doc2k:
 	case DOC_ChipID_Doc2k:
 		reg = DoC_2k_ECCStatus;
 		reg = DoC_2k_ECCStatus;
 		break;
 		break;
@@ -1564,15 +1558,13 @@ static int __init doc_probe(unsigned long physadr)
 			ReadDOC(virtadr, Mplus_Power);
 			ReadDOC(virtadr, Mplus_Power);
 
 
 		/* Reset the Millennium Plus ASIC */
 		/* Reset the Millennium Plus ASIC */
-		tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
-			DOC_MODE_BDECT;
+		tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
 		WriteDOC(tmp, virtadr, Mplus_DOCControl);
 		WriteDOC(tmp, virtadr, Mplus_DOCControl);
 		WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
 		WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
 
 
 		mdelay(1);
 		mdelay(1);
 		/* Enable the Millennium Plus ASIC */
 		/* Enable the Millennium Plus ASIC */
-		tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
-			DOC_MODE_BDECT;
+		tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
 		WriteDOC(tmp, virtadr, Mplus_DOCControl);
 		WriteDOC(tmp, virtadr, Mplus_DOCControl);
 		WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
 		WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
 		mdelay(1);
 		mdelay(1);
@@ -1596,7 +1588,7 @@ static int __init doc_probe(unsigned long physadr)
 		goto notfound;
 		goto notfound;
 	}
 	}
 	/* Check the TOGGLE bit in the ECC register */
 	/* Check the TOGGLE bit in the ECC register */
-	tmp  = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+	tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
 	tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
 	tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
 	tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
 	tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
 	if ((tmp == tmpb) || (tmp != tmpc)) {
 	if ((tmp == tmpb) || (tmp != tmpc)) {
@@ -1626,11 +1618,11 @@ static int __init doc_probe(unsigned long physadr)
 		if (ChipID == DOC_ChipID_DocMilPlus16) {
 		if (ChipID == DOC_ChipID_DocMilPlus16) {
 			WriteDOC(~newval, virtadr, Mplus_AliasResolution);
 			WriteDOC(~newval, virtadr, Mplus_AliasResolution);
 			oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
 			oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
-			WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it
+			WriteDOC(newval, virtadr, Mplus_AliasResolution);	// restore it
 		} else {
 		} else {
 			WriteDOC(~newval, virtadr, AliasResolution);
 			WriteDOC(~newval, virtadr, AliasResolution);
 			oldval = ReadDOC(doc->virtadr, AliasResolution);
 			oldval = ReadDOC(doc->virtadr, AliasResolution);
-			WriteDOC(newval, virtadr, AliasResolution); // restore it
+			WriteDOC(newval, virtadr, AliasResolution);	// restore it
 		}
 		}
 		newval = ~newval;
 		newval = ~newval;
 		if (oldval == newval) {
 		if (oldval == newval) {
@@ -1642,10 +1634,8 @@ static int __init doc_probe(unsigned long physadr)
 	printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
 	printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", 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));
-	mtd =  kmalloc(len, GFP_KERNEL);
+	    sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr));
+	mtd = kmalloc(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;
@@ -1663,17 +1653,19 @@ static int __init doc_probe(unsigned long physadr)
 
 
 	nand->priv		= doc;
 	nand->priv		= doc;
 	nand->select_chip	= doc200x_select_chip;
 	nand->select_chip	= doc200x_select_chip;
-	nand->hwcontrol		= doc200x_hwcontrol;
+	nand->cmd_ctrl		= doc200x_hwcontrol;
 	nand->dev_ready		= doc200x_dev_ready;
 	nand->dev_ready		= doc200x_dev_ready;
 	nand->waitfunc		= doc200x_wait;
 	nand->waitfunc		= doc200x_wait;
 	nand->block_bad		= doc200x_block_bad;
 	nand->block_bad		= doc200x_block_bad;
-	nand->enable_hwecc	= doc200x_enable_hwecc;
-	nand->calculate_ecc	= doc200x_calculate_ecc;
-	nand->correct_data	= doc200x_correct_data;
+	nand->ecc.hwctl		= doc200x_enable_hwecc;
+	nand->ecc.calculate	= doc200x_calculate_ecc;
+	nand->ecc.correct	= doc200x_correct_data;
 
 
-	nand->autooob		= &doc200x_oobinfo;
-	nand->eccmode		= NAND_ECC_HW6_512;
-	nand->options		= NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
+	nand->ecc.layout	= &doc200x_oobinfo;
+	nand->ecc.mode		= NAND_ECC_HW_SYNDROME;
+	nand->ecc.size		= 512;
+	nand->ecc.bytes		= 6;
+	nand->options		= NAND_USE_FLASH_BBT;
 
 
 	doc->physadr		= physadr;
 	doc->physadr		= physadr;
 	doc->virtadr		= virtadr;
 	doc->virtadr		= virtadr;
@@ -1707,18 +1699,18 @@ static int __init doc_probe(unsigned long physadr)
 	doclist = mtd;
 	doclist = mtd;
 	return 0;
 	return 0;
 
 
-notfound:
+ notfound:
 	/* Put back the contents of the DOCControl register, in case it's not
 	/* Put back the contents of the DOCControl register, in case it's not
 	   actually a DiskOnChip.  */
 	   actually a DiskOnChip.  */
 	WriteDOC(save_control, virtadr, DOCControl);
 	WriteDOC(save_control, virtadr, DOCControl);
-fail:
+ fail:
 	iounmap(virtadr);
 	iounmap(virtadr);
 	return ret;
 	return ret;
 }
 }
 
 
 static void release_nanddoc(void)
 static void release_nanddoc(void)
 {
 {
- 	struct mtd_info *mtd, *nextmtd;
+	struct mtd_info *mtd, *nextmtd;
 	struct nand_chip *nand;
 	struct nand_chip *nand;
 	struct doc_priv *doc;
 	struct doc_priv *doc;
 
 
@@ -1747,8 +1739,8 @@ static int __init init_nanddoc(void)
 	 * generator polinomial degree = 4
 	 * generator polinomial degree = 4
 	 */
 	 */
 	rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
 	rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
- 	if (!rs_decoder) {
-		printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n");
+	if (!rs_decoder) {
+		printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
@@ -1758,7 +1750,7 @@ static int __init init_nanddoc(void)
 		if (ret < 0)
 		if (ret < 0)
 			goto outerr;
 			goto outerr;
 	} else {
 	} else {
-		for (i=0; (doc_locations[i] != 0xffffffff); i++) {
+		for (i = 0; (doc_locations[i] != 0xffffffff); i++) {
 			doc_probe(doc_locations[i]);
 			doc_probe(doc_locations[i]);
 		}
 		}
 	}
 	}
@@ -1770,7 +1762,7 @@ static int __init init_nanddoc(void)
 		goto outerr;
 		goto outerr;
 	}
 	}
 	return 0;
 	return 0;
-outerr:
+ outerr:
 	free_rs(rs_decoder);
 	free_rs(rs_decoder);
 	return ret;
 	return ret;
 }
 }

+ 44 - 53
drivers/mtd/nand/edb7312.c

@@ -1,7 +1,7 @@
 /*
 /*
  *  drivers/mtd/nand/edb7312.c
  *  drivers/mtd/nand/edb7312.c
  *
  *
- *  Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ *  Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
  *
  *
  *  Derived from drivers/mtd/nand/autcpu12.c
  *  Derived from drivers/mtd/nand/autcpu12.c
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
@@ -25,7 +25,7 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/partitions.h>
 #include <asm/io.h>
 #include <asm/io.h>
-#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
+#include <asm/arch/hardware.h>	/* for CLPS7111_VIRT_BASE */
 #include <asm/sizes.h>
 #include <asm/sizes.h>
 #include <asm/hardware/clps7111.h>
 #include <asm/hardware/clps7111.h>
 
 
@@ -54,51 +54,45 @@ static struct mtd_info *ep7312_mtd = NULL;
  */
  */
 
 
 static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE;
 static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE;
-static void __iomem * ep7312_pxdr = (void __iomem *) EP7312_PXDR;
-static void __iomem * ep7312_pxddr = (void __iomem *) EP7312_PXDDR;
+static void __iomem *ep7312_pxdr = (void __iomem *)EP7312_PXDR;
+static void __iomem *ep7312_pxddr = (void __iomem *)EP7312_PXDDR;
 
 
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
 /*
 /*
  * Define static partitions for flash device
  * Define static partitions for flash device
  */
  */
 static struct mtd_partition partition_info[] = {
 static struct mtd_partition partition_info[] = {
-	{ .name = "EP7312 Nand Flash",
-		  .offset = 0,
-		  .size = 8*1024*1024 }
+	{.name = "EP7312 Nand Flash",
+	 .offset = 0,
+	 .size = 8 * 1024 * 1024}
 };
 };
+
 #define NUM_PARTITIONS 1
 #define NUM_PARTITIONS 1
 
 
 #endif
 #endif
 
 
-
 /*
 /*
  *	hardware specific access to control-lines
  *	hardware specific access to control-lines
+ *
+ *	NAND_NCE: bit 0 -> bit 7
+ *	NAND_CLE: bit 1 -> bit 4
+ *	NAND_ALE: bit 2 -> bit 5
  */
  */
-static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd)
+static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 {
 {
-	switch(cmd) {
-
-	case NAND_CTL_SETCLE:
-		clps_writeb(clps_readb(ep7312_pxdr) | 0x10, ep7312_pxdr);
-		break;
-	case NAND_CTL_CLRCLE:
-		clps_writeb(clps_readb(ep7312_pxdr) & ~0x10, ep7312_pxdr);
-		break;
-
-	case NAND_CTL_SETALE:
-		clps_writeb(clps_readb(ep7312_pxdr) | 0x20, ep7312_pxdr);
-		break;
-	case NAND_CTL_CLRALE:
-		clps_writeb(clps_readb(ep7312_pxdr) & ~0x20, ep7312_pxdr);
-		break;
-
-	case NAND_CTL_SETNCE:
-		clps_writeb((clps_readb(ep7312_pxdr) | 0x80) & ~0x40, ep7312_pxdr);
-		break;
-	case NAND_CTL_CLRNCE:
-		clps_writeb((clps_readb(ep7312_pxdr) | 0x80) | 0x40, ep7312_pxdr);
-		break;
+	struct nand_chip *chip = mtd->priv;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned char bits;
+
+		bits = (ctrl & (NAND_CLE | NAND_ALE)) << 3;
+		bits = (ctrl & NAND_NCE) << 7;
+
+		clps_writeb((clps_readb(ep7312_pxdr)  & 0xB0) | 0x10,
+			    ep7312_pxdr);
 	}
 	}
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 }
 
 
 /*
 /*
@@ -108,6 +102,7 @@ static int ep7312_device_ready(struct mtd_info *mtd)
 {
 {
 	return 1;
 	return 1;
 }
 }
+
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
 const char *part_probes[] = { "cmdlinepart", NULL };
 const char *part_probes[] = { "cmdlinepart", NULL };
 #endif
 #endif
@@ -115,18 +110,16 @@ const char *part_probes[] = { "cmdlinepart", NULL };
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-static int __init ep7312_init (void)
+static int __init ep7312_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
 	const char *part_type = 0;
 	const char *part_type = 0;
 	int mtd_parts_nb = 0;
 	int mtd_parts_nb = 0;
 	struct mtd_partition *mtd_parts = 0;
 	struct mtd_partition *mtd_parts = 0;
-	void __iomem * ep7312_fio_base;
+	void __iomem *ep7312_fio_base;
 
 
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	ep7312_mtd = kmalloc(sizeof(struct mtd_info) +
-			     sizeof(struct nand_chip),
-			     GFP_KERNEL);
+	ep7312_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!ep7312_mtd) {
 	if (!ep7312_mtd) {
 		printk("Unable to allocate EDB7312 NAND MTD device structure.\n");
 		printk("Unable to allocate EDB7312 NAND MTD device structure.\n");
 		return -ENOMEM;
 		return -ENOMEM;
@@ -134,21 +127,22 @@ static int __init ep7312_init (void)
 
 
 	/* map physical adress */
 	/* map physical adress */
 	ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K);
 	ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K);
-	if(!ep7312_fio_base) {
+	if (!ep7312_fio_base) {
 		printk("ioremap EDB7312 NAND flash failed\n");
 		printk("ioremap EDB7312 NAND flash failed\n");
 		kfree(ep7312_mtd);
 		kfree(ep7312_mtd);
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&ep7312_mtd[1]);
+	this = (struct nand_chip *)(&ep7312_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) ep7312_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(ep7312_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	ep7312_mtd->priv = this;
 	ep7312_mtd->priv = this;
+	ep7312_mtd->owner = THIS_MODULE;
 
 
 	/*
 	/*
 	 * Set GPIO Port B control register so that the pins are configured
 	 * Set GPIO Port B control register so that the pins are configured
@@ -159,22 +153,20 @@ static int __init ep7312_init (void)
 	/* insert callbacks */
 	/* insert callbacks */
 	this->IO_ADDR_R = ep7312_fio_base;
 	this->IO_ADDR_R = ep7312_fio_base;
 	this->IO_ADDR_W = ep7312_fio_base;
 	this->IO_ADDR_W = ep7312_fio_base;
-	this->hwcontrol = ep7312_hwcontrol;
+	this->cmd_ctrl = ep7312_hwcontrol;
 	this->dev_ready = ep7312_device_ready;
 	this->dev_ready = ep7312_device_ready;
 	/* 15 us command delay time */
 	/* 15 us command delay time */
 	this->chip_delay = 15;
 	this->chip_delay = 15;
 
 
 	/* Scan to find existence of the device */
 	/* Scan to find existence of the device */
-	if (nand_scan (ep7312_mtd, 1)) {
+	if (nand_scan(ep7312_mtd, 1)) {
 		iounmap((void *)ep7312_fio_base);
 		iounmap((void *)ep7312_fio_base);
-		kfree (ep7312_mtd);
+		kfree(ep7312_mtd);
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
-
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
 	ep7312_mtd->name = "edb7312-nand";
 	ep7312_mtd->name = "edb7312-nand";
-	mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
-					    &mtd_parts, 0);
+	mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, &mtd_parts, 0);
 	if (mtd_parts_nb > 0)
 	if (mtd_parts_nb > 0)
 		part_type = "command line";
 		part_type = "command line";
 	else
 	else
@@ -193,24 +185,23 @@ static int __init ep7312_init (void)
 	/* Return happy */
 	/* Return happy */
 	return 0;
 	return 0;
 }
 }
+
 module_init(ep7312_init);
 module_init(ep7312_init);
 
 
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-static void __exit ep7312_cleanup (void)
+static void __exit ep7312_cleanup(void)
 {
 {
-	struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1];
+	struct nand_chip *this = (struct nand_chip *)&ep7312_mtd[1];
 
 
 	/* Release resources, unregister device */
 	/* Release resources, unregister device */
-	nand_release (ap7312_mtd);
-
-	/* Free internal data buffer */
-	kfree (this->data_buf);
+	nand_release(ap7312_mtd);
 
 
 	/* Free the MTD device structure */
 	/* Free the MTD device structure */
-	kfree (ep7312_mtd);
+	kfree(ep7312_mtd);
 }
 }
+
 module_exit(ep7312_cleanup);
 module_exit(ep7312_cleanup);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 39 - 59
drivers/mtd/nand/h1910.c

@@ -4,7 +4,7 @@
  *  Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
  *  Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
  *
  *
  *  Derived from drivers/mtd/nand/edb7312.c
  *  Derived from drivers/mtd/nand/edb7312.c
- *       Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ *       Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *
  *
  * $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $
  * $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $
@@ -26,7 +26,7 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/partitions.h>
 #include <asm/io.h>
 #include <asm/io.h>
-#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
+#include <asm/arch/hardware.h>	/* for CLPS7111_VIRT_BASE */
 #include <asm/sizes.h>
 #include <asm/sizes.h>
 #include <asm/arch/h1900-gpio.h>
 #include <asm/arch/h1900-gpio.h>
 #include <asm/arch/ipaq.h>
 #include <asm/arch/ipaq.h>
@@ -45,47 +45,29 @@ static struct mtd_info *h1910_nand_mtd = NULL;
  * Define static partitions for flash device
  * Define static partitions for flash device
  */
  */
 static struct mtd_partition partition_info[] = {
 static struct mtd_partition partition_info[] = {
-	{ name: "h1910 NAND Flash",
-		  offset: 0,
-		  size: 16*1024*1024 }
+      {name:"h1910 NAND Flash",
+	      offset:0,
+      size:16 * 1024 * 1024}
 };
 };
+
 #define NUM_PARTITIONS 1
 #define NUM_PARTITIONS 1
 
 
 #endif
 #endif
 
 
-
 /*
 /*
  *	hardware specific access to control-lines
  *	hardware specific access to control-lines
+ *
+ *	NAND_NCE: bit 0 - don't care
+ *	NAND_CLE: bit 1 - address bit 2
+ *	NAND_ALE: bit 2 - address bit 3
  */
  */
-static void h1910_hwcontrol(struct mtd_info *mtd, int cmd)
+static void h1910_hwcontrol(struct mtd_info *mtd, int cmd,
+			    unsigned int ctrl)
 {
 {
-	struct nand_chip* this = (struct nand_chip *) (mtd->priv);
-
-	switch(cmd) {
-
-	case NAND_CTL_SETCLE:
-		this->IO_ADDR_R |= (1 << 2);
-		this->IO_ADDR_W |= (1 << 2);
-		break;
-	case NAND_CTL_CLRCLE:
-		this->IO_ADDR_R &= ~(1 << 2);
-		this->IO_ADDR_W &= ~(1 << 2);
-		break;
-
-	case NAND_CTL_SETALE:
-		this->IO_ADDR_R |= (1 << 3);
-		this->IO_ADDR_W |= (1 << 3);
-		break;
-	case NAND_CTL_CLRALE:
-		this->IO_ADDR_R &= ~(1 << 3);
-		this->IO_ADDR_W &= ~(1 << 3);
-		break;
-
-	case NAND_CTL_SETNCE:
-		break;
-	case NAND_CTL_CLRNCE:
-		break;
-	}
+	struct nand_chip *chip = mtd->priv;
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W | ((ctrl & 0x6) << 1));
 }
 }
 
 
 /*
 /*
@@ -101,7 +83,7 @@ static int h1910_device_ready(struct mtd_info *mtd)
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-static int __init h1910_init (void)
+static int __init h1910_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
 	const char *part_type = 0;
 	const char *part_type = 0;
@@ -119,24 +101,23 @@ static int __init h1910_init (void)
 	}
 	}
 
 
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) +
-			     sizeof(struct nand_chip),
-			     GFP_KERNEL);
+	h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!h1910_nand_mtd) {
 	if (!h1910_nand_mtd) {
 		printk("Unable to allocate h1910 NAND MTD device structure.\n");
 		printk("Unable to allocate h1910 NAND MTD device structure.\n");
-		iounmap ((void *) nandaddr);
+		iounmap((void *)nandaddr);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&h1910_nand_mtd[1]);
+	this = (struct nand_chip *)(&h1910_nand_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(h1910_nand_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	h1910_nand_mtd->priv = this;
 	h1910_nand_mtd->priv = this;
+	h1910_nand_mtd->owner = THIS_MODULE;
 
 
 	/*
 	/*
 	 * Enable VPEN
 	 * Enable VPEN
@@ -146,31 +127,28 @@ static int __init h1910_init (void)
 	/* insert callbacks */
 	/* insert callbacks */
 	this->IO_ADDR_R = nandaddr;
 	this->IO_ADDR_R = nandaddr;
 	this->IO_ADDR_W = nandaddr;
 	this->IO_ADDR_W = nandaddr;
-	this->hwcontrol = h1910_hwcontrol;
+	this->cmd_ctrl = h1910_hwcontrol;
 	this->dev_ready = NULL;	/* unknown whether that was correct or not so we will just do it like this */
 	this->dev_ready = NULL;	/* unknown whether that was correct or not so we will just do it like this */
 	/* 15 us command delay time */
 	/* 15 us command delay time */
 	this->chip_delay = 50;
 	this->chip_delay = 50;
-	this->eccmode = NAND_ECC_SOFT;
+	this->ecc.mode = NAND_ECC_SOFT;
 	this->options = NAND_NO_AUTOINCR;
 	this->options = NAND_NO_AUTOINCR;
 
 
 	/* Scan to find existence of the device */
 	/* Scan to find existence of the device */
-	if (nand_scan (h1910_nand_mtd, 1)) {
+	if (nand_scan(h1910_nand_mtd, 1)) {
 		printk(KERN_NOTICE "No NAND device - returning -ENXIO\n");
 		printk(KERN_NOTICE "No NAND device - returning -ENXIO\n");
-		kfree (h1910_nand_mtd);
-		iounmap ((void *) nandaddr);
+		kfree(h1910_nand_mtd);
+		iounmap((void *)nandaddr);
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
-
 #ifdef CONFIG_MTD_CMDLINE_PARTS
 #ifdef CONFIG_MTD_CMDLINE_PARTS
-	mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts,
-						"h1910-nand");
+	mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, "h1910-nand");
 	if (mtd_parts_nb > 0)
 	if (mtd_parts_nb > 0)
-	  part_type = "command line";
+		part_type = "command line";
 	else
 	else
-	  mtd_parts_nb = 0;
+		mtd_parts_nb = 0;
 #endif
 #endif
-	if (mtd_parts_nb == 0)
-	{
+	if (mtd_parts_nb == 0) {
 		mtd_parts = partition_info;
 		mtd_parts = partition_info;
 		mtd_parts_nb = NUM_PARTITIONS;
 		mtd_parts_nb = NUM_PARTITIONS;
 		part_type = "static";
 		part_type = "static";
@@ -183,24 +161,26 @@ static int __init h1910_init (void)
 	/* Return happy */
 	/* Return happy */
 	return 0;
 	return 0;
 }
 }
+
 module_init(h1910_init);
 module_init(h1910_init);
 
 
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-static void __exit h1910_cleanup (void)
+static void __exit h1910_cleanup(void)
 {
 {
-	struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1];
+	struct nand_chip *this = (struct nand_chip *)&h1910_nand_mtd[1];
 
 
 	/* Release resources, unregister device */
 	/* Release resources, unregister device */
-	nand_release (h1910_nand_mtd);
+	nand_release(h1910_nand_mtd);
 
 
 	/* Release io resource */
 	/* Release io resource */
-	iounmap ((void *) this->IO_ADDR_W);
+	iounmap((void *)this->IO_ADDR_W);
 
 
 	/* Free the MTD device structure */
 	/* Free the MTD device structure */
-	kfree (h1910_nand_mtd);
+	kfree(h1910_nand_mtd);
 }
 }
+
 module_exit(h1910_cleanup);
 module_exit(h1910_cleanup);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 1529 - 1740
drivers/mtd/nand/nand_base.c

@@ -10,67 +10,31 @@
  *	http://www.linux-mtd.infradead.org/tech/nand.html
  *	http://www.linux-mtd.infradead.org/tech/nand.html
  *
  *
  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
- * 		  2002 Thomas Gleixner (tglx@linutronix.de)
+ *		  2002-2006 Thomas Gleixner (tglx@linutronix.de)
  *
  *
- *  02-08-2004  tglx: support for strange chips, which cannot auto increment
- *		pages on read / read_oob
- *
- *  03-17-2004  tglx: Check ready before auto increment check. Simon Bayes
- *		pointed this out, as he marked an auto increment capable chip
- *		as NOAUTOINCR in the board driver.
- *		Make reads over block boundaries work too
- *
- *  04-14-2004	tglx: first working version for 2k page size chips
- *
- *  05-19-2004  tglx: Basic support for Renesas AG-AND chips
- *
- *  09-24-2004  tglx: add support for hardware controllers (e.g. ECC) shared
- *		among multiple independend devices. Suggestions and initial patch
- *		from Ben Dooks <ben-mtd@fluff.org>
- *
- *  12-05-2004	dmarlin: add workaround for Renesas AG-AND chips "disturb" issue.
- *		Basically, any block not rewritten may lose data when surrounding blocks
- *		are rewritten many times.  JFFS2 ensures this doesn't happen for blocks
- *		it uses, but the Bad Block Table(s) may not be rewritten.  To ensure they
- *		do not lose data, force them to be rewritten when some of the surrounding
- *		blocks are erased.  Rather than tracking a specific nearby block (which
- *		could itself go bad), use a page address 'mask' to select several blocks
- *		in the same area, and rewrite the BBT when any of them are erased.
- *
- *  01-03-2005	dmarlin: added support for the device recovery command sequence for Renesas
- *		AG-AND chips.  If there was a sudden loss of power during an erase operation,
- * 		a "device recovery" operation must be performed when power is restored
- * 		to ensure correct operation.
- *
- *  01-20-2005	dmarlin: added support for optional hardware specific callback routine to
- *		perform extra error status checks on erase and write failures.  This required
- *		adding a wrapper function for nand_read_ecc.
- *
- * 08-20-2005	vwool: suspend/resume added
- *
- * Credits:
+ *  Credits:
  *	David Woodhouse for adding multichip support
  *	David Woodhouse for adding multichip support
  *
  *
  *	Aleph One Ltd. and Toby Churchill Ltd. for supporting the
  *	Aleph One Ltd. and Toby Churchill Ltd. for supporting the
  *	rework for 2K page size chips
  *	rework for 2K page size chips
  *
  *
- * TODO:
+ *  TODO:
  *	Enable cached programming for 2k page size chips
  *	Enable cached programming for 2k page size chips
  *	Check, if mtd->ecctype should be set to MTD_ECC_HW
  *	Check, if mtd->ecctype should be set to MTD_ECC_HW
  *	if we have HW ecc support.
  *	if we have HW ecc support.
  *	The AG-AND chips have nice features for speed improvement,
  *	The AG-AND chips have nice features for speed improvement,
  *	which are not supported yet. Read / program 4 pages in one go.
  *	which are not supported yet. Read / program 4 pages in one go.
  *
  *
- * $Id: nand_base.c,v 1.150 2005/09/15 13:58:48 vwool Exp $
- *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
  *
  *
  */
  */
 
 
+#include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/errno.h>
+#include <linux/err.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/types.h>
@@ -88,75 +52,46 @@
 #endif
 #endif
 
 
 /* Define default oob placement schemes for large and small page devices */
 /* Define default oob placement schemes for large and small page devices */
-static struct nand_oobinfo nand_oob_8 = {
-	.useecc = MTD_NANDECC_AUTOPLACE,
+static struct nand_ecclayout nand_oob_8 = {
 	.eccbytes = 3,
 	.eccbytes = 3,
 	.eccpos = {0, 1, 2},
 	.eccpos = {0, 1, 2},
-	.oobfree = { {3, 2}, {6, 2} }
+	.oobfree = {
+		{.offset = 3,
+		 .length = 2},
+		{.offset = 6,
+		 .length = 2}}
 };
 };
 
 
-static struct nand_oobinfo nand_oob_16 = {
-	.useecc = MTD_NANDECC_AUTOPLACE,
+static struct nand_ecclayout nand_oob_16 = {
 	.eccbytes = 6,
 	.eccbytes = 6,
 	.eccpos = {0, 1, 2, 3, 6, 7},
 	.eccpos = {0, 1, 2, 3, 6, 7},
-	.oobfree = { {8, 8} }
+	.oobfree = {
+		{.offset = 8,
+		 . length = 8}}
 };
 };
 
 
-static struct nand_oobinfo nand_oob_64 = {
-	.useecc = MTD_NANDECC_AUTOPLACE,
+static struct nand_ecclayout nand_oob_64 = {
 	.eccbytes = 24,
 	.eccbytes = 24,
 	.eccpos = {
 	.eccpos = {
-		40, 41, 42, 43, 44, 45, 46, 47,
-		48, 49, 50, 51, 52, 53, 54, 55,
-		56, 57, 58, 59, 60, 61, 62, 63},
-	.oobfree = { {2, 38} }
+		   40, 41, 42, 43, 44, 45, 46, 47,
+		   48, 49, 50, 51, 52, 53, 54, 55,
+		   56, 57, 58, 59, 60, 61, 62, 63},
+	.oobfree = {
+		{.offset = 2,
+		 .length = 38}}
 };
 };
 
 
-/* This is used for padding purposes in nand_write_oob */
-static u_char ffchars[] = {
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-};
+static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
+			   int new_state);
+
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops);
 
 
 /*
 /*
- * NAND low-level MTD interface functions
+ * For devices which display every fart in the system on a seperate LED. Is
+ * compiled away when LED support is disabled.
  */
  */
-static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
-static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
-static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
-
-static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
-static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
-			  size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
-static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
-static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
-static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
-			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
-static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
-static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
-			unsigned long count, loff_t to, size_t * retlen);
-static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs,
-			unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
-static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
-static void nand_sync (struct mtd_info *mtd);
-
-/* Some internal functions */
-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,
-		struct nand_oobinfo *oobsel, int mode);
-#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
-static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
-	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode);
-#else
-#define nand_verify_pages(...) (0)
-#endif
-
-static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);
+DEFINE_LED_TRIGGER(nand_led_trigger);
 
 
 /**
 /**
  * nand_release_device - [GENERIC] release chip
  * nand_release_device - [GENERIC] release chip
@@ -164,27 +99,19 @@ static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int ne
  *
  *
  * Deselect, release chip lock and wake up anyone waiting on the device
  * Deselect, release chip lock and wake up anyone waiting on the device
  */
  */
-static void nand_release_device (struct mtd_info *mtd)
+static void nand_release_device(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
 	/* De-select the NAND device */
 	/* De-select the NAND device */
-	this->select_chip(mtd, -1);
-
-	if (this->controller) {
-		/* Release the controller and the chip */
-		spin_lock(&this->controller->lock);
-		this->controller->active = NULL;
-		this->state = FL_READY;
-		wake_up(&this->controller->wq);
-		spin_unlock(&this->controller->lock);
-	} else {
-		/* Release the chip */
-		spin_lock(&this->chip_lock);
-		this->state = FL_READY;
-		wake_up(&this->wq);
-		spin_unlock(&this->chip_lock);
-	}
+	chip->select_chip(mtd, -1);
+
+	/* Release the controller and the chip */
+	spin_lock(&chip->controller->lock);
+	chip->controller->active = NULL;
+	chip->state = FL_READY;
+	wake_up(&chip->controller->wq);
+	spin_unlock(&chip->controller->lock);
 }
 }
 
 
 /**
 /**
@@ -193,23 +120,10 @@ static void nand_release_device (struct mtd_info *mtd)
  *
  *
  * Default read function for 8bit buswith
  * Default read function for 8bit buswith
  */
  */
-static u_char nand_read_byte(struct mtd_info *mtd)
-{
-	struct nand_chip *this = mtd->priv;
-	return readb(this->IO_ADDR_R);
-}
-
-/**
- * nand_write_byte - [DEFAULT] write one byte to the chip
- * @mtd:	MTD device structure
- * @byte:	pointer to data byte to write
- *
- * Default write function for 8it buswith
- */
-static void nand_write_byte(struct mtd_info *mtd, u_char byte)
+static uint8_t nand_read_byte(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
-	writeb(byte, this->IO_ADDR_W);
+	struct nand_chip *chip = mtd->priv;
+	return readb(chip->IO_ADDR_R);
 }
 }
 
 
 /**
 /**
@@ -219,24 +133,10 @@ static void nand_write_byte(struct mtd_info *mtd, u_char byte)
  * Default read function for 16bit buswith with
  * Default read function for 16bit buswith with
  * endianess conversion
  * endianess conversion
  */
  */
-static u_char nand_read_byte16(struct mtd_info *mtd)
-{
-	struct nand_chip *this = mtd->priv;
-	return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
-}
-
-/**
- * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip
- * @mtd:	MTD device structure
- * @byte:	pointer to data byte to write
- *
- * Default write function for 16bit buswith with
- * endianess conversion
- */
-static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
+static uint8_t nand_read_byte16(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
-	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+	struct nand_chip *chip = mtd->priv;
+	return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
 }
 }
 
 
 /**
 /**
@@ -248,22 +148,8 @@ static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
  */
  */
 static u16 nand_read_word(struct mtd_info *mtd)
 static u16 nand_read_word(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
-	return readw(this->IO_ADDR_R);
-}
-
-/**
- * nand_write_word - [DEFAULT] write one word to the chip
- * @mtd:	MTD device structure
- * @word:	data word to write
- *
- * Default write function for 16bit buswith without
- * endianess conversion
- */
-static void nand_write_word(struct mtd_info *mtd, u16 word)
-{
-	struct nand_chip *this = mtd->priv;
-	writew(word, this->IO_ADDR_W);
+	struct nand_chip *chip = mtd->priv;
+	return readw(chip->IO_ADDR_R);
 }
 }
 
 
 /**
 /**
@@ -273,15 +159,15 @@ static void nand_write_word(struct mtd_info *mtd, u16 word)
  *
  *
  * Default select function for 1 chip devices.
  * Default select function for 1 chip devices.
  */
  */
-static void nand_select_chip(struct mtd_info *mtd, int chip)
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
 {
 {
-	struct nand_chip *this = mtd->priv;
-	switch(chip) {
+	struct nand_chip *chip = mtd->priv;
+
+	switch (chipnr) {
 	case -1:
 	case -1:
-		this->hwcontrol(mtd, NAND_CTL_CLRNCE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
 		break;
 		break;
 	case 0:
 	case 0:
-		this->hwcontrol(mtd, NAND_CTL_SETNCE);
 		break;
 		break;
 
 
 	default:
 	default:
@@ -297,13 +183,13 @@ static void nand_select_chip(struct mtd_info *mtd, int chip)
  *
  *
  * Default write function for 8bit buswith
  * Default write function for 8bit buswith
  */
  */
-static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 {
 {
 	int i;
 	int i;
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
-	for (i=0; i<len; i++)
-		writeb(buf[i], this->IO_ADDR_W);
+	for (i = 0; i < len; i++)
+		writeb(buf[i], chip->IO_ADDR_W);
 }
 }
 
 
 /**
 /**
@@ -314,13 +200,13 @@ static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
  *
  *
  * Default read function for 8bit buswith
  * Default read function for 8bit buswith
  */
  */
-static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
 {
 	int i;
 	int i;
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
-	for (i=0; i<len; i++)
-		buf[i] = readb(this->IO_ADDR_R);
+	for (i = 0; i < len; i++)
+		buf[i] = readb(chip->IO_ADDR_R);
 }
 }
 
 
 /**
 /**
@@ -331,15 +217,14 @@ static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
  *
  *
  * Default verify function for 8bit buswith
  * Default verify function for 8bit buswith
  */
  */
-static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 {
 {
 	int i;
 	int i;
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
-	for (i=0; i<len; i++)
-		if (buf[i] != readb(this->IO_ADDR_R))
+	for (i = 0; i < len; i++)
+		if (buf[i] != readb(chip->IO_ADDR_R))
 			return -EFAULT;
 			return -EFAULT;
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -351,15 +236,15 @@ static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
  *
  *
  * Default write function for 16bit buswith
  * Default write function for 16bit buswith
  */
  */
-static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
 {
 {
 	int i;
 	int i;
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	u16 *p = (u16 *) buf;
 	u16 *p = (u16 *) buf;
 	len >>= 1;
 	len >>= 1;
 
 
-	for (i=0; i<len; i++)
-		writew(p[i], this->IO_ADDR_W);
+	for (i = 0; i < len; i++)
+		writew(p[i], chip->IO_ADDR_W);
 
 
 }
 }
 
 
@@ -371,15 +256,15 @@ static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
  *
  *
  * Default read function for 16bit buswith
  * Default read function for 16bit buswith
  */
  */
-static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
 {
 {
 	int i;
 	int i;
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	u16 *p = (u16 *) buf;
 	u16 *p = (u16 *) buf;
 	len >>= 1;
 	len >>= 1;
 
 
-	for (i=0; i<len; i++)
-		p[i] = readw(this->IO_ADDR_R);
+	for (i = 0; i < len; i++)
+		p[i] = readw(chip->IO_ADDR_R);
 }
 }
 
 
 /**
 /**
@@ -390,15 +275,15 @@ static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
  *
  *
  * Default verify function for 16bit buswith
  * Default verify function for 16bit buswith
  */
  */
-static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
 {
 {
 	int i;
 	int i;
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	u16 *p = (u16 *) buf;
 	u16 *p = (u16 *) buf;
 	len >>= 1;
 	len >>= 1;
 
 
-	for (i=0; i<len; i++)
-		if (p[i] != readw(this->IO_ADDR_R))
+	for (i = 0; i < len; i++)
+		if (p[i] != readw(chip->IO_ADDR_R))
 			return -EFAULT;
 			return -EFAULT;
 
 
 	return 0;
 	return 0;
@@ -415,38 +300,37 @@ static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
 static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 {
 {
 	int page, chipnr, res = 0;
 	int page, chipnr, res = 0;
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	u16 bad;
 	u16 bad;
 
 
 	if (getchip) {
 	if (getchip) {
-		page = (int)(ofs >> this->page_shift);
-		chipnr = (int)(ofs >> this->chip_shift);
+		page = (int)(ofs >> chip->page_shift);
+		chipnr = (int)(ofs >> chip->chip_shift);
 
 
-		/* Grab the lock and see if the device is available */
-		nand_get_device (this, mtd, FL_READING);
+		nand_get_device(chip, mtd, FL_READING);
 
 
 		/* Select the NAND device */
 		/* Select the NAND device */
-		this->select_chip(mtd, chipnr);
+		chip->select_chip(mtd, chipnr);
 	} else
 	} else
-		page = (int) ofs;
+		page = (int)ofs;
 
 
-	if (this->options & NAND_BUSWIDTH_16) {
-		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);
-		bad = cpu_to_le16(this->read_word(mtd));
-		if (this->badblockpos & 0x1)
+	if (chip->options & NAND_BUSWIDTH_16) {
+		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
+			      page & chip->pagemask);
+		bad = cpu_to_le16(chip->read_word(mtd));
+		if (chip->badblockpos & 0x1)
 			bad >>= 8;
 			bad >>= 8;
 		if ((bad & 0xFF) != 0xff)
 		if ((bad & 0xFF) != 0xff)
 			res = 1;
 			res = 1;
 	} else {
 	} else {
-		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);
-		if (this->read_byte(mtd) != 0xff)
+		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
+			      page & chip->pagemask);
+		if (chip->read_byte(mtd) != 0xff)
 			res = 1;
 			res = 1;
 	}
 	}
 
 
-	if (getchip) {
-		/* Deselect and wake up anyone waiting on the device */
+	if (getchip)
 		nand_release_device(mtd);
 		nand_release_device(mtd);
-	}
 
 
 	return res;
 	return res;
 }
 }
@@ -461,23 +345,33 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 */
 */
 static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
 static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
 {
-	struct nand_chip *this = mtd->priv;
-	u_char buf[2] = {0, 0};
-	size_t	retlen;
-	int block;
+	struct nand_chip *chip = mtd->priv;
+	uint8_t buf[2] = { 0, 0 };
+	int block, ret;
 
 
 	/* Get block number */
 	/* Get block number */
-	block = ((int) ofs) >> this->bbt_erase_shift;
-	if (this->bbt)
-		this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+	block = ((int)ofs) >> chip->bbt_erase_shift;
+	if (chip->bbt)
+		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
 
 
 	/* Do we have a flash based bad block table ? */
 	/* Do we have a flash based bad block table ? */
-	if (this->options & NAND_USE_FLASH_BBT)
-		return nand_update_bbt (mtd, ofs);
+	if (chip->options & NAND_USE_FLASH_BBT)
+		ret = nand_update_bbt(mtd, ofs);
+	else {
+		/* We write two bytes, so we dont have to mess with 16 bit
+		 * access
+		 */
+		ofs += mtd->oobsize;
+		chip->ops.len = 2;
+		chip->ops.datbuf = NULL;
+		chip->ops.oobbuf = buf;
+		chip->ops.ooboffs = chip->badblockpos & ~0x01;
 
 
-	/* We write two bytes, so we dont have to mess with 16 bit access */
-	ofs += mtd->oobsize + (this->badblockpos & ~0x01);
-	return nand_write_oob (mtd, ofs , 2, &retlen, buf);
+		ret = nand_do_write_oob(mtd, ofs, &chip->ops);
+	}
+	if (!ret)
+		mtd->ecc_stats.badblocks++;
+	return ret;
 }
 }
 
 
 /**
 /**
@@ -487,12 +381,12 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
  *
  *
  * The function expects, that the device is already selected
  * The function expects, that the device is already selected
  */
  */
-static int nand_check_wp (struct mtd_info *mtd)
+static int nand_check_wp(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	/* Check the WP bit */
 	/* Check the WP bit */
-	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
-	return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
+	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+	return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
 }
 }
 
 
 /**
 /**
@@ -505,32 +399,31 @@ static int nand_check_wp (struct mtd_info *mtd)
  * Check, if the block is bad. Either by reading the bad block table or
  * Check, if the block is bad. Either by reading the bad block table or
  * calling of the scan function.
  * calling of the scan function.
  */
  */
-static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+			       int allowbbt)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
-	if (!this->bbt)
-		return this->block_bad(mtd, ofs, getchip);
+	if (!chip->bbt)
+		return chip->block_bad(mtd, ofs, getchip);
 
 
 	/* Return info from the table */
 	/* Return info from the table */
-	return nand_isbad_bbt (mtd, ofs, allowbbt);
+	return nand_isbad_bbt(mtd, ofs, allowbbt);
 }
 }
 
 
-DEFINE_LED_TRIGGER(nand_led_trigger);
-
 /*
 /*
  * Wait for the ready pin, after a command
  * Wait for the ready pin, after a command
  * The timeout is catched later.
  * The timeout is catched later.
  */
  */
 static void nand_wait_ready(struct mtd_info *mtd)
 static void nand_wait_ready(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
-	unsigned long	timeo = jiffies + 2;
+	struct nand_chip *chip = mtd->priv;
+	unsigned long timeo = jiffies + 2;
 
 
 	led_trigger_event(nand_led_trigger, LED_FULL);
 	led_trigger_event(nand_led_trigger, LED_FULL);
 	/* wait until command is processed or timeout occures */
 	/* wait until command is processed or timeout occures */
 	do {
 	do {
-		if (this->dev_ready(mtd))
+		if (chip->dev_ready(mtd))
 			break;
 			break;
 		touch_softlockup_watchdog();
 		touch_softlockup_watchdog();
 	} while (time_before(jiffies, timeo));
 	} while (time_before(jiffies, timeo));
@@ -547,21 +440,21 @@ static void nand_wait_ready(struct mtd_info *mtd)
  * Send command to NAND device. This function is used for small page
  * Send command to NAND device. This function is used for small page
  * devices (256/512 Bytes per page)
  * devices (256/512 Bytes per page)
  */
  */
-static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+			 int column, int page_addr)
 {
 {
-	register struct nand_chip *this = mtd->priv;
+	register struct nand_chip *chip = mtd->priv;
+	int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
 
 
-	/* Begin command latch cycle */
-	this->hwcontrol(mtd, NAND_CTL_SETCLE);
 	/*
 	/*
 	 * Write out the command to the device.
 	 * Write out the command to the device.
 	 */
 	 */
 	if (command == NAND_CMD_SEQIN) {
 	if (command == NAND_CMD_SEQIN) {
 		int readcmd;
 		int readcmd;
 
 
-		if (column >= mtd->oobblock) {
+		if (column >= mtd->writesize) {
 			/* OOB area */
 			/* OOB area */
-			column -= mtd->oobblock;
+			column -= mtd->writesize;
 			readcmd = NAND_CMD_READOOB;
 			readcmd = NAND_CMD_READOOB;
 		} else if (column < 256) {
 		} else if (column < 256) {
 			/* First 256 bytes --> READ0 */
 			/* First 256 bytes --> READ0 */
@@ -570,38 +463,37 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
 			column -= 256;
 			column -= 256;
 			readcmd = NAND_CMD_READ1;
 			readcmd = NAND_CMD_READ1;
 		}
 		}
-		this->write_byte(mtd, readcmd);
+		chip->cmd_ctrl(mtd, readcmd, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
 	}
 	}
-	this->write_byte(mtd, command);
+	chip->cmd_ctrl(mtd, command, ctrl);
 
 
-	/* Set ALE and clear CLE to start address cycle */
-	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
-
-	if (column != -1 || page_addr != -1) {
-		this->hwcontrol(mtd, NAND_CTL_SETALE);
-
-		/* Serially input address */
-		if (column != -1) {
-			/* Adjust columns for 16 bit buswidth */
-			if (this->options & NAND_BUSWIDTH_16)
-				column >>= 1;
-			this->write_byte(mtd, column);
-		}
-		if (page_addr != -1) {
-			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
-			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
-			/* One more address cycle for devices > 32MiB */
-			if (this->chipsize > (32 << 20))
-				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
-		}
-		/* Latch in address */
-		this->hwcontrol(mtd, NAND_CTL_CLRALE);
+	/*
+	 * Address cycle, when necessary
+	 */
+	ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
+	/* Serially input address */
+	if (column != -1) {
+		/* Adjust columns for 16 bit buswidth */
+		if (chip->options & NAND_BUSWIDTH_16)
+			column >>= 1;
+		chip->cmd_ctrl(mtd, column, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
 	}
 	}
+	if (page_addr != -1) {
+		chip->cmd_ctrl(mtd, page_addr, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
+		chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);
+		/* One more address cycle for devices > 32MiB */
+		if (chip->chipsize > (32 << 20))
+			chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);
+	}
+	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 
 
 	/*
 	/*
 	 * program and erase have their own busy handlers
 	 * program and erase have their own busy handlers
 	 * status and sequential in needs no delay
 	 * status and sequential in needs no delay
-	*/
+	 */
 	switch (command) {
 	switch (command) {
 
 
 	case NAND_CMD_PAGEPROG:
 	case NAND_CMD_PAGEPROG:
@@ -612,29 +504,30 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
 		return;
 		return;
 
 
 	case NAND_CMD_RESET:
 	case NAND_CMD_RESET:
-		if (this->dev_ready)
+		if (chip->dev_ready)
 			break;
 			break;
-		udelay(this->chip_delay);
-		this->hwcontrol(mtd, NAND_CTL_SETCLE);
-		this->write_byte(mtd, NAND_CMD_STATUS);
-		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
-		while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
+		udelay(chip->chip_delay);
+		chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+			       NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd,
+			       NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+		while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
 		return;
 		return;
 
 
-	/* This applies to read commands */
+		/* This applies to read commands */
 	default:
 	default:
 		/*
 		/*
 		 * If we don't have access to the busy pin, we apply the given
 		 * If we don't have access to the busy pin, we apply the given
 		 * command delay
 		 * command delay
-		*/
-		if (!this->dev_ready) {
-			udelay (this->chip_delay);
+		 */
+		if (!chip->dev_ready) {
+			udelay(chip->chip_delay);
 			return;
 			return;
 		}
 		}
 	}
 	}
 	/* Apply this short delay always to ensure that we do wait tWB in
 	/* Apply this short delay always to ensure that we do wait tWB in
 	 * any case on any machine. */
 	 * any case on any machine. */
-	ndelay (100);
+	ndelay(100);
 
 
 	nand_wait_ready(mtd);
 	nand_wait_ready(mtd);
 }
 }
@@ -646,50 +539,49 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
  * @column:	the column address for this command, -1 if none
  * @column:	the column address for this command, -1 if none
  * @page_addr:	the page address for this command, -1 if none
  * @page_addr:	the page address for this command, -1 if none
  *
  *
- * Send command to NAND device. This is the version for the new large page devices
- * We dont have the seperate regions as we have in the small page devices.
- * We must emulate NAND_CMD_READOOB to keep the code compatible.
+ * Send command to NAND device. This is the version for the new large page
+ * devices We dont have the separate regions as we have in the small page
+ * devices.  We must emulate NAND_CMD_READOOB to keep the code compatible.
  *
  *
  */
  */
-static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+			    int column, int page_addr)
 {
 {
-	register struct nand_chip *this = mtd->priv;
+	register struct nand_chip *chip = mtd->priv;
 
 
 	/* Emulate NAND_CMD_READOOB */
 	/* Emulate NAND_CMD_READOOB */
 	if (command == NAND_CMD_READOOB) {
 	if (command == NAND_CMD_READOOB) {
-		column += mtd->oobblock;
+		column += mtd->writesize;
 		command = NAND_CMD_READ0;
 		command = NAND_CMD_READ0;
 	}
 	}
 
 
-
-	/* Begin command latch cycle */
-	this->hwcontrol(mtd, NAND_CTL_SETCLE);
-	/* Write out the command to the device. */
-	this->write_byte(mtd, (command & 0xff));
-	/* End command latch cycle */
-	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+	/* Command latch cycle */
+	chip->cmd_ctrl(mtd, command & 0xff,
+		       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
 
 
 	if (column != -1 || page_addr != -1) {
 	if (column != -1 || page_addr != -1) {
-		this->hwcontrol(mtd, NAND_CTL_SETALE);
+		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
 
 
 		/* Serially input address */
 		/* Serially input address */
 		if (column != -1) {
 		if (column != -1) {
 			/* Adjust columns for 16 bit buswidth */
 			/* Adjust columns for 16 bit buswidth */
-			if (this->options & NAND_BUSWIDTH_16)
+			if (chip->options & NAND_BUSWIDTH_16)
 				column >>= 1;
 				column >>= 1;
-			this->write_byte(mtd, column & 0xff);
-			this->write_byte(mtd, column >> 8);
+			chip->cmd_ctrl(mtd, column, ctrl);
+			ctrl &= ~NAND_CTRL_CHANGE;
+			chip->cmd_ctrl(mtd, column >> 8, ctrl);
 		}
 		}
 		if (page_addr != -1) {
 		if (page_addr != -1) {
-			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
-			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+			chip->cmd_ctrl(mtd, page_addr, ctrl);
+			chip->cmd_ctrl(mtd, page_addr >> 8,
+				       NAND_NCE | NAND_ALE);
 			/* One more address cycle for devices > 128MiB */
 			/* One more address cycle for devices > 128MiB */
-			if (this->chipsize > (128 << 20))
-				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
+			if (chip->chipsize > (128 << 20))
+				chip->cmd_ctrl(mtd, page_addr >> 16,
+					       NAND_NCE | NAND_ALE);
 		}
 		}
-		/* Latch in address */
-		this->hwcontrol(mtd, NAND_CTL_CLRALE);
 	}
 	}
+	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 
 
 	/*
 	/*
 	 * program and erase have their own busy handlers
 	 * program and erase have their own busy handlers
@@ -702,55 +594,62 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
 	case NAND_CMD_ERASE1:
 	case NAND_CMD_ERASE1:
 	case NAND_CMD_ERASE2:
 	case NAND_CMD_ERASE2:
 	case NAND_CMD_SEQIN:
 	case NAND_CMD_SEQIN:
+	case NAND_CMD_RNDIN:
 	case NAND_CMD_STATUS:
 	case NAND_CMD_STATUS:
 	case NAND_CMD_DEPLETE1:
 	case NAND_CMD_DEPLETE1:
 		return;
 		return;
 
 
-	/*
-	 * read error status commands require only a short delay
-	 */
+		/*
+		 * read error status commands require only a short delay
+		 */
 	case NAND_CMD_STATUS_ERROR:
 	case NAND_CMD_STATUS_ERROR:
 	case NAND_CMD_STATUS_ERROR0:
 	case NAND_CMD_STATUS_ERROR0:
 	case NAND_CMD_STATUS_ERROR1:
 	case NAND_CMD_STATUS_ERROR1:
 	case NAND_CMD_STATUS_ERROR2:
 	case NAND_CMD_STATUS_ERROR2:
 	case NAND_CMD_STATUS_ERROR3:
 	case NAND_CMD_STATUS_ERROR3:
-		udelay(this->chip_delay);
+		udelay(chip->chip_delay);
 		return;
 		return;
 
 
 	case NAND_CMD_RESET:
 	case NAND_CMD_RESET:
-		if (this->dev_ready)
+		if (chip->dev_ready)
 			break;
 			break;
-		udelay(this->chip_delay);
-		this->hwcontrol(mtd, NAND_CTL_SETCLE);
-		this->write_byte(mtd, NAND_CMD_STATUS);
-		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
-		while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
+		udelay(chip->chip_delay);
+		chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+			       NAND_NCE | NAND_CTRL_CHANGE);
+		while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
+		return;
+
+	case NAND_CMD_RNDOUT:
+		/* No ready / busy check necessary */
+		chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
+			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+			       NAND_NCE | NAND_CTRL_CHANGE);
 		return;
 		return;
 
 
 	case NAND_CMD_READ0:
 	case NAND_CMD_READ0:
-		/* Begin command latch cycle */
-		this->hwcontrol(mtd, NAND_CTL_SETCLE);
-		/* Write out the start read command */
-		this->write_byte(mtd, NAND_CMD_READSTART);
-		/* End command latch cycle */
-		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
-		/* Fall through into ready check */
-
-	/* This applies to read commands */
+		chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+			       NAND_NCE | NAND_CTRL_CHANGE);
+
+		/* This applies to read commands */
 	default:
 	default:
 		/*
 		/*
 		 * If we don't have access to the busy pin, we apply the given
 		 * If we don't have access to the busy pin, we apply the given
 		 * command delay
 		 * command delay
-		*/
-		if (!this->dev_ready) {
-			udelay (this->chip_delay);
+		 */
+		if (!chip->dev_ready) {
+			udelay(chip->chip_delay);
 			return;
 			return;
 		}
 		}
 	}
 	}
 
 
 	/* Apply this short delay always to ensure that we do wait tWB in
 	/* Apply this short delay always to ensure that we do wait tWB in
 	 * any case on any machine. */
 	 * any case on any machine. */
-	ndelay (100);
+	ndelay(100);
 
 
 	nand_wait_ready(mtd);
 	nand_wait_ready(mtd);
 }
 }
@@ -763,34 +662,28 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
  *
  *
  * Get the device and lock it for exclusive access
  * Get the device and lock it for exclusive access
  */
  */
-static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+static int
+nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
 {
 {
-	struct nand_chip *active;
-	spinlock_t *lock;
-	wait_queue_head_t *wq;
-	DECLARE_WAITQUEUE (wait, current);
-
-	lock = (this->controller) ? &this->controller->lock : &this->chip_lock;
-	wq = (this->controller) ? &this->controller->wq : &this->wq;
-retry:
-	active = this;
+	spinlock_t *lock = &chip->controller->lock;
+	wait_queue_head_t *wq = &chip->controller->wq;
+	DECLARE_WAITQUEUE(wait, current);
+ retry:
 	spin_lock(lock);
 	spin_lock(lock);
 
 
 	/* Hardware controller shared among independend devices */
 	/* Hardware controller shared among independend devices */
-	if (this->controller) {
-		if (this->controller->active)
-			active = this->controller->active;
-		else
-			this->controller->active = this;
-	}
-	if (active == this && this->state == FL_READY) {
-		this->state = new_state;
+	/* Hardware controller shared among independend devices */
+	if (!chip->controller->active)
+		chip->controller->active = chip;
+
+	if (chip->controller->active == chip && chip->state == FL_READY) {
+		chip->state = new_state;
 		spin_unlock(lock);
 		spin_unlock(lock);
 		return 0;
 		return 0;
 	}
 	}
 	if (new_state == FL_PM_SUSPENDED) {
 	if (new_state == FL_PM_SUSPENDED) {
 		spin_unlock(lock);
 		spin_unlock(lock);
-		return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
+		return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
 	}
 	}
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	add_wait_queue(wq, &wait);
 	add_wait_queue(wq, &wait);
@@ -804,540 +697,339 @@ retry:
  * nand_wait - [DEFAULT]  wait until the command is done
  * nand_wait - [DEFAULT]  wait until the command is done
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
  * @this:	NAND chip structure
  * @this:	NAND chip structure
- * @state:	state to select the max. timeout value
  *
  *
  * Wait for command done. This applies to erase and program only
  * Wait for command done. This applies to erase and program only
  * Erase can take up to 400ms and program up to 20ms according to
  * Erase can take up to 400ms and program up to 20ms according to
  * general NAND and SmartMedia specs
  * general NAND and SmartMedia specs
  *
  *
 */
 */
-static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
 {
 {
 
 
-	unsigned long	timeo = jiffies;
-	int	status;
+	unsigned long timeo = jiffies;
+	int status, state = chip->state;
 
 
 	if (state == FL_ERASING)
 	if (state == FL_ERASING)
-		 timeo += (HZ * 400) / 1000;
+		timeo += (HZ * 400) / 1000;
 	else
 	else
-		 timeo += (HZ * 20) / 1000;
+		timeo += (HZ * 20) / 1000;
 
 
 	led_trigger_event(nand_led_trigger, LED_FULL);
 	led_trigger_event(nand_led_trigger, LED_FULL);
 
 
 	/* Apply this short delay always to ensure that we do wait tWB in
 	/* Apply this short delay always to ensure that we do wait tWB in
 	 * any case on any machine. */
 	 * any case on any machine. */
-	ndelay (100);
+	ndelay(100);
 
 
-	if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
-		this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
+	if ((state == FL_ERASING) && (chip->options & NAND_IS_AND))
+		chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
 	else
 	else
-		this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+		chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
 
 
 	while (time_before(jiffies, timeo)) {
 	while (time_before(jiffies, timeo)) {
-		/* Check, if we were interrupted */
-		if (this->state != state)
-			return 0;
-
-		if (this->dev_ready) {
-			if (this->dev_ready(mtd))
+		if (chip->dev_ready) {
+			if (chip->dev_ready(mtd))
 				break;
 				break;
 		} else {
 		} else {
-			if (this->read_byte(mtd) & NAND_STATUS_READY)
+			if (chip->read_byte(mtd) & NAND_STATUS_READY)
 				break;
 				break;
 		}
 		}
 		cond_resched();
 		cond_resched();
 	}
 	}
 	led_trigger_event(nand_led_trigger, LED_OFF);
 	led_trigger_event(nand_led_trigger, LED_OFF);
 
 
-	status = (int) this->read_byte(mtd);
+	status = (int)chip->read_byte(mtd);
 	return status;
 	return status;
 }
 }
 
 
 /**
 /**
- * nand_write_page - [GENERIC] write one page
- * @mtd:	MTD device structure
- * @this:	NAND chip structure
- * @page: 	startpage inside the chip, must be called with (page & this->pagemask)
- * @oob_buf:	out of band data buffer
- * @oobsel:	out of band selecttion structre
- * @cached:	1 = enable cached programming if supported by chip
- *
- * Nand_page_program function is used for write and writev !
- * This function will always program a full page of data
- * If you call it with a non page aligned buffer, you're lost :)
- *
- * Cached programming is not supported yet.
+ * nand_read_page_raw - [Intern] read raw page data without ecc
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
  */
  */
-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page,
-	u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached)
+static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+			      uint8_t *buf)
 {
 {
-	int 	i, status;
-	u_char	ecc_code[32];
-	int	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
-	int  	*oob_config = oobsel->eccpos;
-	int	datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
-	int	eccbytes = 0;
-
-	/* FIXME: Enable cached programming */
-	cached = 0;
-
-	/* Send command to begin auto page programming */
-	this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
+	chip->read_buf(mtd, buf, mtd->writesize);
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	return 0;
+}
 
 
-	/* Write out complete page of data, take care of eccmode */
-	switch (eccmode) {
-	/* No ecc, write all */
-	case NAND_ECC_NONE:
-		printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
-		this->write_buf(mtd, this->data_poi, mtd->oobblock);
-		break;
+/**
+ * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ */
+static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf)
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *ecc_calc = chip->buffers.ecccalc;
+	uint8_t *ecc_code = chip->buffers.ecccode;
+	int *eccpos = chip->ecc.layout->eccpos;
 
 
-	/* Software ecc 3/256, write all */
-	case NAND_ECC_SOFT:
-		for (; eccsteps; eccsteps--) {
-			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
-			for (i = 0; i < 3; i++, eccidx++)
-				oob_buf[oob_config[eccidx]] = ecc_code[i];
-			datidx += this->eccsize;
-		}
-		this->write_buf(mtd, this->data_poi, mtd->oobblock);
-		break;
-	default:
-		eccbytes = this->eccbytes;
-		for (; eccsteps; eccsteps--) {
-			/* enable hardware ecc logic for write */
-			this->enable_hwecc(mtd, NAND_ECC_WRITE);
-			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
-			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
-			for (i = 0; i < eccbytes; i++, eccidx++)
-				oob_buf[oob_config[eccidx]] = ecc_code[i];
-			/* If the hardware ecc provides syndromes then
-			 * the ecc code must be written immidiately after
-			 * the data bytes (words) */
-			if (this->options & NAND_HWECC_SYNDROME)
-				this->write_buf(mtd, ecc_code, eccbytes);
-			datidx += this->eccsize;
-		}
-		break;
-	}
+	nand_read_page_raw(mtd, chip, buf);
 
 
-	/* Write out OOB data */
-	if (this->options & NAND_HWECC_SYNDROME)
-		this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
-	else
-		this->write_buf(mtd, oob_buf, mtd->oobsize);
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
 
-	/* Send command to actually program the data */
-	this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
+	for (i = 0; i < chip->ecc.total; i++)
+		ecc_code[i] = chip->oob_poi[eccpos[i]];
 
 
-	if (!cached) {
-		/* call wait ready function */
-		status = this->waitfunc (mtd, this, FL_WRITING);
+	eccsteps = chip->ecc.steps;
+	p = buf;
 
 
-		/* See if operation failed and additional status checks are available */
-		if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
-			status = this->errstat(mtd, this, FL_WRITING, status, page);
-		}
+	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
 
 
-		/* See if device thinks it succeeded */
-		if (status & NAND_STATUS_FAIL) {
-			DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
-			return -EIO;
-		}
-	} else {
-		/* FIXME: Implement cached programming ! */
-		/* wait until cache is ready*/
-		// status = this->waitfunc (mtd, this, FL_CACHEDRPG);
+		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+		if (stat == -1)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
 	}
 	}
 	return 0;
 	return 0;
 }
 }
 
 
-#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
 /**
 /**
- * nand_verify_pages - [GENERIC] verify the chip contents after a write
- * @mtd:	MTD device structure
- * @this:	NAND chip structure
- * @page: 	startpage inside the chip, must be called with (page & this->pagemask)
- * @numpages:	number of pages to verify
- * @oob_buf:	out of band data buffer
- * @oobsel:	out of band selecttion structre
- * @chipnr:	number of the current chip
- * @oobmode:	1 = full buffer verify, 0 = ecc only
+ * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
  *
  *
- * The NAND device assumes that it is always writing to a cleanly erased page.
- * Hence, it performs its internal write verification only on bits that
- * transitioned from 1 to 0. The device does NOT verify the whole page on a
- * byte by byte basis. It is possible that the page was not completely erased
- * or the page is becoming unusable due to wear. The read with ECC would catch
- * the error later when the ECC page check fails, but we would rather catch
- * it early in the page write stage. Better to write no data than invalid data.
+ * Not for syndrome calculating ecc controllers which need a special oob layout
  */
  */
-static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
-	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
+static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf)
 {
 {
-	int 	i, j, datidx = 0, oobofs = 0, res = -EIO;
-	int	eccsteps = this->eccsteps;
-	int	hweccbytes;
-	u_char 	oobdata[64];
-
-	hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
-
-	/* Send command to read back the first page */
-	this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
-
-	for(;;) {
-		for (j = 0; j < eccsteps; j++) {
-			/* Loop through and verify the data */
-			if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) {
-				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
-				goto out;
-			}
-			datidx += mtd->eccsize;
-			/* Have we a hw generator layout ? */
-			if (!hweccbytes)
-				continue;
-			if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) {
-				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
-				goto out;
-			}
-			oobofs += hweccbytes;
-		}
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *ecc_calc = chip->buffers.ecccalc;
+	uint8_t *ecc_code = chip->buffers.ecccode;
+	int *eccpos = chip->ecc.layout->eccpos;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		chip->ecc.hwctl(mtd, NAND_ECC_READ);
+		chip->read_buf(mtd, p, eccsize);
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+	}
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 
 
-		/* check, if we must compare all data or if we just have to
-		 * compare the ecc bytes
-		 */
-		if (oobmode) {
-			if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
-				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
-				goto out;
-			}
-		} else {
-			/* Read always, else autoincrement fails */
-			this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
-
-			if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
-				int ecccnt = oobsel->eccbytes;
-
-				for (i = 0; i < ecccnt; i++) {
-					int idx = oobsel->eccpos[i];
-					if (oobdata[idx] != oob_buf[oobofs + idx] ) {
-						DEBUG (MTD_DEBUG_LEVEL0,
-					       	"%s: Failed ECC write "
-						"verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
-						goto out;
-					}
-				}
-			}
-		}
-		oobofs += mtd->oobsize - hweccbytes * eccsteps;
-		page++;
-		numpages--;
-
-		/* Apply delay or wait for ready/busy pin
-		 * Do this before the AUTOINCR check, so no problems
-		 * arise if a chip which does auto increment
-		 * is marked as NOAUTOINCR by the board driver.
-		 * Do this also before returning, so the chip is
-		 * ready for the next command.
-		*/
-		if (!this->dev_ready)
-			udelay (this->chip_delay);
-		else
-			nand_wait_ready(mtd);
+	for (i = 0; i < chip->ecc.total; i++)
+		ecc_code[i] = chip->oob_poi[eccpos[i]];
 
 
-		/* All done, return happy */
-		if (!numpages)
-			return 0;
+	eccsteps = chip->ecc.steps;
+	p = buf;
 
 
+	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
 
 
-		/* Check, if the chip supports auto page increment */
-		if (!NAND_CANAUTOINCR(this))
-			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+		if (stat == -1)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
 	}
 	}
-	/*
-	 * Terminate the read command. We come here in case of an error
-	 * So we must issue a reset command.
-	 */
-out:
-	this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1);
-	return res;
+	return 0;
 }
 }
-#endif
 
 
 /**
 /**
- * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
- * @mtd:	MTD device structure
- * @from:	offset to read from
- * @len:	number of bytes to read
- * @retlen:	pointer to variable to store the number of read bytes
- * @buf:	the databuffer to put data
+ * 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
  *
  *
- * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL
- * and flags = 0xff
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
  */
  */
-static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+				   uint8_t *buf)
 {
 {
-	return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff);
-}
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *oob = chip->oob_poi;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
+
+		chip->ecc.hwctl(mtd, NAND_ECC_READ);
+		chip->read_buf(mtd, p, eccsize);
+
+		if (chip->ecc.prepad) {
+			chip->read_buf(mtd, oob, chip->ecc.prepad);
+			oob += chip->ecc.prepad;
+		}
+
+		chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
+		chip->read_buf(mtd, oob, eccbytes);
+		stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+		if (stat == -1)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
 
 
+		oob += eccbytes;
+
+		if (chip->ecc.postpad) {
+			chip->read_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
+		}
+	}
+
+	/* Calculate remaining oob bytes */
+	i = mtd->oobsize - (oob - chip->oob_poi);
+	if (i)
+		chip->read_buf(mtd, oob, i);
+
+	return 0;
+}
 
 
 /**
 /**
- * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc
- * @mtd:	MTD device structure
- * @from:	offset to read from
- * @len:	number of bytes to read
- * @retlen:	pointer to variable to store the number of read bytes
- * @buf:	the databuffer to put data
- * @oob_buf:	filesystem supplied oob data buffer
- * @oobsel:	oob selection structure
- *
- * This function simply calls nand_do_read_ecc with flags = 0xff
+ * nand_transfer_oob - [Internal] Transfer oob to client buffer
+ * @chip:	nand chip structure
+ * @ops:	oob ops structure
  */
  */
-static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
-			  size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
+static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+				  struct mtd_oob_ops *ops)
 {
 {
-	/* use userspace supplied oobinfo, if zero */
-	if (oobsel == NULL)
-		oobsel = &mtd->oobinfo;
-	return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff);
+	size_t len = ops->ooblen;
+
+	switch(ops->mode) {
+
+	case MTD_OOB_PLACE:
+	case MTD_OOB_RAW:
+		memcpy(oob, chip->oob_poi + ops->ooboffs, len);
+		return oob + len;
+
+	case MTD_OOB_AUTO: {
+		struct nand_oobfree *free = chip->ecc.layout->oobfree;
+		uint32_t boffs = 0, roffs = ops->ooboffs;
+		size_t bytes = 0;
+
+		for(; free->length && len; free++, len -= bytes) {
+			/* Read request not from offset 0 ? */
+			if (unlikely(roffs)) {
+				if (roffs >= free->length) {
+					roffs -= free->length;
+					continue;
+				}
+				boffs = free->offset + roffs;
+				bytes = min_t(size_t, len,
+					      (free->length - roffs));
+				roffs = 0;
+			} else {
+				bytes = min_t(size_t, len, free->length);
+				boffs = free->offset;
+			}
+			memcpy(oob, chip->oob_poi + boffs, bytes);
+			oob += bytes;
+		}
+		return oob;
+	}
+	default:
+		BUG();
+	}
+	return NULL;
 }
 }
 
 
-
 /**
 /**
- * nand_do_read_ecc - [MTD Interface] Read data with ECC
+ * nand_do_read_ops - [Internal] Read data with ECC
+ *
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
  * @from:	offset to read from
  * @from:	offset to read from
- * @len:	number of bytes to read
- * @retlen:	pointer to variable to store the number of read bytes
- * @buf:	the databuffer to put data
- * @oob_buf:	filesystem supplied oob data buffer (can be NULL)
- * @oobsel:	oob selection structure
- * @flags:	flag to indicate if nand_get_device/nand_release_device should be preformed
- *		and how many corrected error bits are acceptable:
- *		  bits 0..7 - number of tolerable errors
- *		  bit  8    - 0 == do not get/release chip, 1 == get/release chip
  *
  *
- * NAND read with ECC
+ * Internal function. Called with chip held.
  */
  */
-int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
-			     size_t * retlen, u_char * buf, u_char * oob_buf,
-			     struct nand_oobinfo *oobsel, int flags)
+static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+			    struct mtd_oob_ops *ops)
 {
 {
+	int chipnr, page, realpage, col, bytes, aligned;
+	struct nand_chip *chip = mtd->priv;
+	struct mtd_ecc_stats stats;
+	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+	int sndcmd = 1;
+	int ret = 0;
+	uint32_t readlen = ops->len;
+	uint8_t *bufpoi, *oob, *buf;
 
 
-	int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
-	int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
-	struct nand_chip *this = mtd->priv;
-	u_char *data_poi, *oob_data = oob_buf;
-	u_char ecc_calc[32];
-	u_char ecc_code[32];
-        int eccmode, eccsteps;
-	int	*oob_config, datidx;
-	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
-	int	eccbytes;
-	int	compareecc = 1;
-	int	oobreadlen;
-
-
-	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
-
-	/* Do not allow reads past end of device */
-	if ((from + len) > mtd->size) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
-		*retlen = 0;
-		return -EINVAL;
-	}
-
-	/* Grab the lock and see if the device is available */
-	if (flags & NAND_GET_DEVICE)
-		nand_get_device (this, mtd, FL_READING);
-
-	/* Autoplace of oob data ? Use the default placement scheme */
-	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
-		oobsel = this->autooob;
-
-	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
-	oob_config = oobsel->eccpos;
-
-	/* Select the NAND device */
-	chipnr = (int)(from >> this->chip_shift);
-	this->select_chip(mtd, chipnr);
-
-	/* First we calculate the starting page */
-	realpage = (int) (from >> this->page_shift);
-	page = realpage & this->pagemask;
-
-	/* Get raw starting column */
-	col = from & (mtd->oobblock - 1);
-
-	end = mtd->oobblock;
-	ecc = this->eccsize;
-	eccbytes = this->eccbytes;
-
-	if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
-		compareecc = 0;
-
-	oobreadlen = mtd->oobsize;
-	if (this->options & NAND_HWECC_SYNDROME)
-		oobreadlen -= oobsel->eccbytes;
+	stats = mtd->ecc_stats;
 
 
-	/* Loop until all data read */
-	while (read < len) {
+	chipnr = (int)(from >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
 
 
-		int aligned = (!col && (len - read) >= end);
-		/*
-		 * If the read is not page aligned, we have to read into data buffer
-		 * due to ecc, else we read into return buffer direct
-		 */
-		if (aligned)
-			data_poi = &buf[read];
-		else
-			data_poi = this->data_buf;
+	realpage = (int)(from >> chip->page_shift);
+	page = realpage & chip->pagemask;
 
 
-		/* Check, if we have this page in the buffer
-		 *
-		 * FIXME: Make it work when we must provide oob data too,
-		 * check the usage of data_buf oob field
-		 */
-		if (realpage == this->pagebuf && !oob_buf) {
-			/* aligned read ? */
-			if (aligned)
-				memcpy (data_poi, this->data_buf, end);
-			goto readdata;
-		}
+	col = (int)(from & (mtd->writesize - 1));
+	chip->oob_poi = chip->buffers.oobrbuf;
 
 
-		/* Check, if we must send the read command */
-		if (sndcmd) {
-			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
-			sndcmd = 0;
-		}
+	buf = ops->datbuf;
+	oob = ops->oobbuf;
 
 
-		/* get oob area, if we have no oob buffer from fs-driver */
-		if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
-			oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
-			oob_data = &this->data_buf[end];
+	while(1) {
+		bytes = min(mtd->writesize - col, readlen);
+		aligned = (bytes == mtd->writesize);
 
 
-		eccsteps = this->eccsteps;
+		/* Is the current page in the buffer ? */
+		if (realpage != chip->pagebuf || oob) {
+			bufpoi = aligned ? buf : chip->buffers.databuf;
 
 
-		switch (eccmode) {
-		case NAND_ECC_NONE: {	/* No ECC, Read in a page */
-			static unsigned long lastwhinge = 0;
-			if ((lastwhinge / HZ) != (jiffies / HZ)) {
-				printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
-				lastwhinge = jiffies;
+			if (likely(sndcmd)) {
+				chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+				sndcmd = 0;
 			}
 			}
-			this->read_buf(mtd, data_poi, end);
-			break;
-		}
 
 
-		case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */
-			this->read_buf(mtd, data_poi, end);
-			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
-				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
-			break;
+			/* Now read the page into the buffer */
+			ret = chip->ecc.read_page(mtd, chip, bufpoi);
+			if (ret < 0)
+				break;
 
 
-		default:
-			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
-				this->enable_hwecc(mtd, NAND_ECC_READ);
-				this->read_buf(mtd, &data_poi[datidx], ecc);
-
-				/* HW ecc with syndrome calculation must read the
-				 * syndrome from flash immidiately after the data */
-				if (!compareecc) {
-					/* Some hw ecc generators need to know when the
-					 * syndrome is read from flash */
-					this->enable_hwecc(mtd, NAND_ECC_READSYN);
-					this->read_buf(mtd, &oob_data[i], eccbytes);
-					/* We calc error correction directly, it checks the hw
-					 * generator for an error, reads back the syndrome and
-					 * does the error correction on the fly */
-					ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]);
-					if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
-						DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
-							"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
-						ecc_failed++;
-					}
-				} else {
-					this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
-				}
+			/* Transfer not aligned data */
+			if (!aligned) {
+				chip->pagebuf = realpage;
+				memcpy(buf, chip->buffers.databuf + col, bytes);
 			}
 			}
-			break;
-		}
-
-		/* read oobdata */
-		this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
-
-		/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
-		if (!compareecc)
-			goto readoob;
 
 
-		/* Pick the ECC bytes out of the oob data */
-		for (j = 0; j < oobsel->eccbytes; j++)
-			ecc_code[j] = oob_data[oob_config[j]];
+			buf += bytes;
 
 
-		/* correct data, if neccecary */
-		for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
-			ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
-
-			/* Get next chunk of ecc bytes */
-			j += eccbytes;
-
-			/* Check, if we have a fs supplied oob-buffer,
-			 * This is the legacy mode. Used by YAFFS1
-			 * Should go away some day
-			 */
-			if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
-				int *p = (int *)(&oob_data[mtd->oobsize]);
-				p[i] = ecc_status;
+			if (unlikely(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 ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
-				DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
-				ecc_failed++;
+			if (!(chip->options & NAND_NO_READRDY)) {
+				/*
+				 * Apply delay or wait for ready/busy pin. Do
+				 * this before the AUTOINCR check, so no
+				 * problems arise if a chip which does auto
+				 * increment is marked as NOAUTOINCR by the
+				 * board driver.
+				 */
+				if (!chip->dev_ready)
+					udelay(chip->chip_delay);
+				else
+					nand_wait_ready(mtd);
 			}
 			}
+		} else {
+			memcpy(buf, chip->buffers.databuf + col, bytes);
+			buf += bytes;
 		}
 		}
 
 
-	readoob:
-		/* check, if we have a fs supplied oob-buffer */
-		if (oob_buf) {
-			/* without autoplace. Legacy mode used by YAFFS1 */
-			switch(oobsel->useecc) {
-			case MTD_NANDECC_AUTOPLACE:
-			case MTD_NANDECC_AUTOPL_USR:
-				/* Walk through the autoplace chunks */
-				for (i = 0; oobsel->oobfree[i][1]; i++) {
-					int from = oobsel->oobfree[i][0];
-					int num = oobsel->oobfree[i][1];
-					memcpy(&oob_buf[oob], &oob_data[from], num);
-					oob += num;
-				}
-				break;
-			case MTD_NANDECC_PLACE:
-				/* YAFFS1 legacy mode */
-				oob_data += this->eccsteps * sizeof (int);
-			default:
-				oob_data += mtd->oobsize;
-			}
-		}
-	readdata:
-		/* Partial page read, transfer data into fs buffer */
-		if (!aligned) {
-			for (j = col; j < end && read < len; j++)
-				buf[read++] = data_poi[j];
-			this->pagebuf = realpage;
-		} else
-			read += mtd->oobblock;
-
-		/* Apply delay or wait for ready/busy pin
-		 * Do this before the AUTOINCR check, so no problems
-		 * arise if a chip which does auto increment
-		 * is marked as NOAUTOINCR by the board driver.
-		*/
-		if (!this->dev_ready)
-			udelay (this->chip_delay);
-		else
-			nand_wait_ready(mtd);
+		readlen -= bytes;
 
 
-		if (read == len)
+		if (!readlen)
 			break;
 			break;
 
 
 		/* For subsequent reads align to page boundary. */
 		/* For subsequent reads align to page boundary. */
@@ -1345,701 +1037,775 @@ int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
 		/* Increment page address */
 		/* Increment page address */
 		realpage++;
 		realpage++;
 
 
-		page = realpage & this->pagemask;
+		page = realpage & chip->pagemask;
 		/* Check, if we cross a chip boundary */
 		/* Check, if we cross a chip boundary */
 		if (!page) {
 		if (!page) {
 			chipnr++;
 			chipnr++;
-			this->select_chip(mtd, -1);
-			this->select_chip(mtd, chipnr);
+			chip->select_chip(mtd, -1);
+			chip->select_chip(mtd, chipnr);
 		}
 		}
+
 		/* Check, if the chip supports auto page increment
 		/* Check, if the chip supports auto page increment
 		 * or if we have hit a block boundary.
 		 * or if we have hit a block boundary.
-		*/
-		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+		 */
+		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
 			sndcmd = 1;
 			sndcmd = 1;
 	}
 	}
 
 
-	/* Deselect and wake up anyone waiting on the device */
-	if (flags & NAND_GET_DEVICE)
-		nand_release_device(mtd);
+	ops->retlen = ops->len - (size_t) readlen;
 
 
-	/*
-	 * Return success, if no ECC failures, else -EBADMSG
-	 * fs driver will take care of that, because
-	 * retlen == desired len and result == -EBADMSG
-	 */
-	*retlen = read;
-	return ecc_failed ? -EBADMSG : 0;
+	if (ret)
+		return ret;
+
+	if (mtd->ecc_stats.failed - stats.failed)
+		return -EBADMSG;
+
+	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
 }
 }
 
 
 /**
 /**
- * nand_read_oob - [MTD Interface] NAND read out-of-band
+ * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
  * @from:	offset to read from
  * @from:	offset to read from
  * @len:	number of bytes to read
  * @len:	number of bytes to read
  * @retlen:	pointer to variable to store the number of read bytes
  * @retlen:	pointer to variable to store the number of read bytes
  * @buf:	the databuffer to put data
  * @buf:	the databuffer to put data
  *
  *
- * NAND read out-of-band data from the spare area
+ * Get hold of the chip and call nand_do_read
  */
  */
-static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+		     size_t *retlen, uint8_t *buf)
 {
 {
-	int i, col, page, chipnr;
-	struct nand_chip *this = mtd->priv;
-	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
-
-	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
-
-	/* Shift to get page */
-	page = (int)(from >> this->page_shift);
-	chipnr = (int)(from >> this->chip_shift);
-
-	/* Mask to get column */
-	col = from & (mtd->oobsize - 1);
-
-	/* Initialize return length value */
-	*retlen = 0;
+	struct nand_chip *chip = mtd->priv;
+	int ret;
 
 
 	/* Do not allow reads past end of device */
 	/* Do not allow reads past end of device */
-	if ((from + len) > mtd->size) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
-		*retlen = 0;
+	if ((from + len) > mtd->size)
 		return -EINVAL;
 		return -EINVAL;
-	}
-
-	/* Grab the lock and see if the device is available */
-	nand_get_device (this, mtd , FL_READING);
-
-	/* Select the NAND device */
-	this->select_chip(mtd, chipnr);
+	if (!len)
+		return 0;
 
 
-	/* Send the read command */
-	this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
-	/*
-	 * Read the data, if we read more than one page
-	 * oob data, let the device transfer the data !
-	 */
-	i = 0;
-	while (i < len) {
-		int thislen = mtd->oobsize - col;
-		thislen = min_t(int, thislen, len);
-		this->read_buf(mtd, &buf[i], thislen);
-		i += thislen;
-
-		/* Read more ? */
-		if (i < len) {
-			page++;
-			col = 0;
-
-			/* Check, if we cross a chip boundary */
-			if (!(page & this->pagemask)) {
-				chipnr++;
-				this->select_chip(mtd, -1);
-				this->select_chip(mtd, chipnr);
-			}
+	nand_get_device(chip, mtd, FL_READING);
 
 
-			/* Apply delay or wait for ready/busy pin
-			 * Do this before the AUTOINCR check, so no problems
-			 * arise if a chip which does auto increment
-			 * is marked as NOAUTOINCR by the board driver.
-			 */
-			if (!this->dev_ready)
-				udelay (this->chip_delay);
-			else
-				nand_wait_ready(mtd);
+	chip->ops.len = len;
+	chip->ops.datbuf = buf;
+	chip->ops.oobbuf = NULL;
 
 
-			/* Check, if the chip supports auto page increment
-			 * or if we have hit a block boundary.
-			*/
-			if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
-				/* For subsequent page reads set offset to 0 */
-			        this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
-			}
-		}
-	}
+	ret = nand_do_read_ops(mtd, from, &chip->ops);
 
 
-	/* Deselect and wake up anyone waiting on the device */
 	nand_release_device(mtd);
 	nand_release_device(mtd);
 
 
-	/* Return happy */
-	*retlen = len;
-	return 0;
+	*retlen = chip->ops.retlen;
+	return ret;
 }
 }
 
 
 /**
 /**
- * nand_read_raw - [GENERIC] Read raw data including oob into buffer
- * @mtd:	MTD device structure
- * @buf:	temporary buffer
- * @from:	offset to read from
- * @len:	number of bytes to read
- * @ooblen:	number of oob data bytes to read
- *
- * Read raw data including oob into buffer
+ * nand_read_oob_std - [REPLACABLE] the most common OOB data read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @page:	page number to read
+ * @sndcmd:	flag whether to issue read command or not
  */
  */
-int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen)
+static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+			     int page, int sndcmd)
 {
 {
-	struct nand_chip *this = mtd->priv;
-	int page = (int) (from >> this->page_shift);
-	int chip = (int) (from >> this->chip_shift);
-	int sndcmd = 1;
-	int cnt = 0;
-	int pagesize = mtd->oobblock + mtd->oobsize;
-	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
-
-	/* Do not allow reads past end of device */
-	if ((from + len) > mtd->size) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n");
-		return -EINVAL;
+	if (sndcmd) {
+		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+		sndcmd = 0;
 	}
 	}
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	return sndcmd;
+}
 
 
-	/* Grab the lock and see if the device is available */
-	nand_get_device (this, mtd , FL_READING);
+/**
+ * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC
+ *			    with syndromes
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @page:	page number to read
+ * @sndcmd:	flag whether to issue read command or not
+ */
+static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+				  int page, int sndcmd)
+{
+	uint8_t *buf = chip->oob_poi;
+	int length = mtd->oobsize;
+	int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+	int eccsize = chip->ecc.size;
+	uint8_t *bufpoi = buf;
+	int i, toread, sndrnd = 0, pos;
+
+	chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
+	for (i = 0; i < chip->ecc.steps; i++) {
+		if (sndrnd) {
+			pos = eccsize + i * (eccsize + chunk);
+			if (mtd->writesize > 512)
+				chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
+			else
+				chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page);
+		} else
+			sndrnd = 1;
+		toread = min_t(int, length, chunk);
+		chip->read_buf(mtd, bufpoi, toread);
+		bufpoi += toread;
+		length -= toread;
+	}
+	if (length > 0)
+		chip->read_buf(mtd, bufpoi, length);
 
 
-	this->select_chip (mtd, chip);
+	return 1;
+}
+
+/**
+ * nand_write_oob_std - [REPLACABLE] the most common OOB data write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @page:	page number to write
+ */
+static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+			      int page)
+{
+	int status = 0;
+	const uint8_t *buf = chip->oob_poi;
+	int length = mtd->oobsize;
 
 
-	/* Add requested oob length */
-	len += ooblen;
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+	chip->write_buf(mtd, buf, length);
+	/* Send command to program the OOB data */
+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
 
 
-	while (len) {
-		if (sndcmd)
-			this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
-		sndcmd = 0;
+	status = chip->waitfunc(mtd, chip);
 
 
-		this->read_buf (mtd, &buf[cnt], pagesize);
+	return status;
+}
 
 
-		len -= pagesize;
-		cnt += pagesize;
-		page++;
+/**
+ * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC
+ *			     with syndrome - only for large page flash !
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @page:	page number to write
+ */
+static int nand_write_oob_syndrome(struct mtd_info *mtd,
+				   struct nand_chip *chip, int page)
+{
+	int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+	int eccsize = chip->ecc.size, length = mtd->oobsize;
+	int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
+	const uint8_t *bufpoi = chip->oob_poi;
 
 
-		if (!this->dev_ready)
-			udelay (this->chip_delay);
-		else
-			nand_wait_ready(mtd);
+	/*
+	 * data-ecc-data-ecc ... ecc-oob
+	 * or
+	 * data-pad-ecc-pad-data-pad .... ecc-pad-oob
+	 */
+	if (!chip->ecc.prepad && !chip->ecc.postpad) {
+		pos = steps * (eccsize + chunk);
+		steps = 0;
+	} else
+		pos = eccsize + chunk;
 
 
-		/* Check, if the chip supports auto page increment */
-		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
+	for (i = 0; i < steps; i++) {
+		if (sndcmd) {
+			if (mtd->writesize <= 512) {
+				uint32_t fill = 0xFFFFFFFF;
+
+				len = eccsize;
+				while (len > 0) {
+					int num = min_t(int, len, 4);
+					chip->write_buf(mtd, (uint8_t *)&fill,
+							num);
+					len -= num;
+				}
+			} else {
+				pos = eccsize + i * (eccsize + chunk);
+				chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);
+			}
+		} else
 			sndcmd = 1;
 			sndcmd = 1;
+		len = min_t(int, length, chunk);
+		chip->write_buf(mtd, bufpoi, len);
+		bufpoi += len;
+		length -= len;
 	}
 	}
+	if (length > 0)
+		chip->write_buf(mtd, bufpoi, length);
 
 
-	/* Deselect and wake up anyone waiting on the device */
-	nand_release_device(mtd);
-	return 0;
-}
+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+	status = chip->waitfunc(mtd, chip);
 
 
+	return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
 
 
 /**
 /**
- * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer
+ * nand_do_read_oob - [Intern] NAND read out-of-band
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
- * @fsbuf:	buffer given by fs driver
- * @oobsel:	out of band selection structre
- * @autoplace:	1 = place given buffer into the oob bytes
- * @numpages:	number of pages to prepare
- *
- * Return:
- * 1. Filesystem buffer available and autoplacement is off,
- *    return filesystem buffer
- * 2. No filesystem buffer or autoplace is off, return internal
- *    buffer
- * 3. Filesystem buffer is given and autoplace selected
- *    put data from fs buffer into internal buffer and
- *    retrun internal buffer
- *
- * Note: The internal buffer is filled with 0xff. This must
- * be done only once, when no autoplacement happens
- * Autoplacement sets the buffer dirty flag, which
- * forces the 0xff fill before using the buffer again.
+ * @from:	offset to read from
+ * @ops:	oob operations description structure
  *
  *
-*/
-static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel,
-		int autoplace, int numpages)
+ * NAND read out-of-band data from the spare area
+ */
+static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+			    struct mtd_oob_ops *ops)
 {
 {
-	struct nand_chip *this = mtd->priv;
-	int i, len, ofs;
-
-	/* Zero copy fs supplied buffer */
-	if (fsbuf && !autoplace)
-		return fsbuf;
-
-	/* Check, if the buffer must be filled with ff again */
-	if (this->oobdirty) {
-		memset (this->oob_buf, 0xff,
-			mtd->oobsize << (this->phys_erase_shift - this->page_shift));
-		this->oobdirty = 0;
-	}
+	int page, realpage, chipnr, sndcmd = 1;
+	struct nand_chip *chip = mtd->priv;
+	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+	int readlen = ops->len;
+	uint8_t *buf = ops->oobbuf;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
+	      (unsigned long long)from, readlen);
+
+	chipnr = (int)(from >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
+
+	/* Shift to get page */
+	realpage = (int)(from >> chip->page_shift);
+	page = realpage & chip->pagemask;
+
+	chip->oob_poi = chip->buffers.oobrbuf;
+
+	while(1) {
+		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
+		buf = nand_transfer_oob(chip, buf, ops);
 
 
-	/* If we have no autoplacement or no fs buffer use the internal one */
-	if (!autoplace || !fsbuf)
-		return this->oob_buf;
-
-	/* Walk through the pages and place the data */
-	this->oobdirty = 1;
-	ofs = 0;
-	while (numpages--) {
-		for (i = 0, len = 0; len < mtd->oobavail; i++) {
-			int to = ofs + oobsel->oobfree[i][0];
-			int num = oobsel->oobfree[i][1];
-			memcpy (&this->oob_buf[to], fsbuf, num);
-			len += num;
-			fsbuf += num;
+		readlen -= ops->ooblen;
+		if (!readlen)
+			break;
+
+		if (!(chip->options & NAND_NO_READRDY)) {
+			/*
+			 * Apply delay or wait for ready/busy pin. Do this
+			 * before the AUTOINCR check, so no problems arise if a
+			 * chip which does auto increment is marked as
+			 * NOAUTOINCR by the board driver.
+			 */
+			if (!chip->dev_ready)
+				udelay(chip->chip_delay);
+			else
+				nand_wait_ready(mtd);
 		}
 		}
-		ofs += mtd->oobavail;
+
+		/* Increment page address */
+		realpage++;
+
+		page = realpage & chip->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!page) {
+			chipnr++;
+			chip->select_chip(mtd, -1);
+			chip->select_chip(mtd, chipnr);
+		}
+
+		/* Check, if the chip supports auto page increment
+		 * or if we have hit a block boundary.
+		 */
+		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
+			sndcmd = 1;
 	}
 	}
-	return this->oob_buf;
-}
 
 
-#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
+	ops->retlen = ops->len;
+	return 0;
+}
 
 
 /**
 /**
- * nand_write - [MTD Interface] compability function for nand_write_ecc
+ * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
- * @to:		offset to write to
- * @len:	number of bytes to write
- * @retlen:	pointer to variable to store the number of written bytes
- * @buf:	the data to write
- *
- * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL
+ * @from:	offset to read from
+ * @ops:	oob operation description structure
  *
  *
-*/
-static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+ * NAND read data and/or out-of-band data
+ */
+static int nand_read_oob(struct mtd_info *mtd, loff_t from,
+			 struct mtd_oob_ops *ops)
 {
 {
-	return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
+	int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
+			 uint8_t *buf) = NULL;
+	struct nand_chip *chip = mtd->priv;
+	int ret = -ENOTSUPP;
+
+	ops->retlen = 0;
+
+	/* Do not allow reads past end of device */
+	if ((from + ops->len) > mtd->size) {
+		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+		      "Attempt read beyond end of device\n");
+		return -EINVAL;
+	}
+
+	nand_get_device(chip, mtd, FL_READING);
+
+	switch(ops->mode) {
+	case MTD_OOB_PLACE:
+	case MTD_OOB_AUTO:
+		break;
+
+	case MTD_OOB_RAW:
+		/* Replace the read_page algorithm temporary */
+		read_page = chip->ecc.read_page;
+		chip->ecc.read_page = nand_read_page_raw;
+		break;
+
+	default:
+		goto out;
+	}
+
+	if (!ops->datbuf)
+		ret = nand_do_read_oob(mtd, from, ops);
+	else
+		ret = nand_do_read_ops(mtd, from, ops);
+
+	if (unlikely(ops->mode == MTD_OOB_RAW))
+		chip->ecc.read_page = read_page;
+ out:
+	nand_release_device(mtd);
+	return ret;
 }
 }
 
 
+
 /**
 /**
- * nand_write_ecc - [MTD Interface] NAND write with ECC
- * @mtd:	MTD device structure
- * @to:		offset to write to
- * @len:	number of bytes to write
- * @retlen:	pointer to variable to store the number of written bytes
- * @buf:	the data to write
- * @eccbuf:	filesystem supplied oob data buffer
- * @oobsel:	oob selection structure
- *
- * NAND write with ECC
+ * nand_write_page_raw - [Intern] raw page write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
  */
  */
-static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
-			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
+static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf)
 {
 {
-	int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr;
-	int autoplace = 0, numpages, totalpages;
-	struct nand_chip *this = mtd->priv;
-	u_char *oobbuf, *bufstart;
-	int	ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+	chip->write_buf(mtd, buf, mtd->writesize);
+	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
 
 
-	DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+/**
+ * nand_write_page_swecc - {REPLACABLE] software ecc based page write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
+ */
+static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+				  const uint8_t *buf)
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *ecc_calc = chip->buffers.ecccalc;
+	const uint8_t *p = buf;
+	int *eccpos = chip->ecc.layout->eccpos;
 
 
-	/* Initialize retlen, in case of early exit */
-	*retlen = 0;
+	/* Software ecc calculation */
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
 
-	/* Do not allow write past end of device */
-	if ((to + len) > mtd->size) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
-		return -EINVAL;
-	}
+	for (i = 0; i < chip->ecc.total; i++)
+		chip->oob_poi[eccpos[i]] = ecc_calc[i];
 
 
-	/* reject writes, which are not page aligned */
-	if (NOTALIGNED (to) || NOTALIGNED(len)) {
-		printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
-		return -EINVAL;
+	nand_write_page_raw(mtd, chip, buf);
+}
+
+/**
+ * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
+ */
+static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+				  const uint8_t *buf)
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *ecc_calc = chip->buffers.ecccalc;
+	const uint8_t *p = buf;
+	int *eccpos = chip->ecc.layout->eccpos;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+		chip->write_buf(mtd, p, eccsize);
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 	}
 	}
 
 
-	/* Grab the lock and see if the device is available */
-	nand_get_device (this, mtd, FL_WRITING);
+	for (i = 0; i < chip->ecc.total; i++)
+		chip->oob_poi[eccpos[i]] = ecc_calc[i];
 
 
-	/* Calculate chipnr */
-	chipnr = (int)(to >> this->chip_shift);
-	/* Select the NAND device */
-	this->select_chip(mtd, chipnr);
+	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
 
 
-	/* Check, if it is write protected */
-	if (nand_check_wp(mtd))
-		goto out;
+/**
+ * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
+ *
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
+ */
+static void nand_write_page_syndrome(struct mtd_info *mtd,
+				    struct nand_chip *chip, const uint8_t *buf)
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	const uint8_t *p = buf;
+	uint8_t *oob = chip->oob_poi;
 
 
-	/* if oobsel is NULL, use chip defaults */
-	if (oobsel == NULL)
-		oobsel = &mtd->oobinfo;
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
 
 
-	/* Autoplace of oob data ? Use the default placement scheme */
-	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
-		oobsel = this->autooob;
-		autoplace = 1;
-	}
-	if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
-		autoplace = 1;
+		chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+		chip->write_buf(mtd, p, eccsize);
 
 
-	/* Setup variables and oob buffer */
-	totalpages = len >> this->page_shift;
-	page = (int) (to >> this->page_shift);
-	/* Invalidate the page cache, if we write to the cached page */
-	if (page <= this->pagebuf && this->pagebuf < (page + totalpages))
-		this->pagebuf = -1;
-
-	/* Set it relative to chip */
-	page &= this->pagemask;
-	startpage = page;
-	/* Calc number of pages we can write in one go */
-	numpages = min (ppblock - (startpage  & (ppblock - 1)), totalpages);
-	oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
-	bufstart = (u_char *)buf;
-
-	/* Loop until all data is written */
-	while (written < len) {
-
-		this->data_poi = (u_char*) &buf[written];
-		/* Write one page. If this is the last page to write
-		 * or the last page in this block, then use the
-		 * real pageprogram command, else select cached programming
-		 * if supported by the chip.
-		 */
-		ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));
-		if (ret) {
-			DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret);
-			goto out;
+		if (chip->ecc.prepad) {
+			chip->write_buf(mtd, oob, chip->ecc.prepad);
+			oob += chip->ecc.prepad;
 		}
 		}
-		/* Next oob page */
-		oob += mtd->oobsize;
-		/* Update written bytes count */
-		written += mtd->oobblock;
-		if (written == len)
-			goto cmp;
 
 
-		/* Increment page address */
-		page++;
-
-		/* Have we hit a block boundary ? Then we have to verify and
-		 * if verify is ok, we have to setup the oob buffer for
-		 * the next pages.
-		*/
-		if (!(page & (ppblock - 1))){
-			int ofs;
-			this->data_poi = bufstart;
-			ret = nand_verify_pages (mtd, this, startpage,
-				page - startpage,
-				oobbuf, oobsel, chipnr, (eccbuf != NULL));
-			if (ret) {
-				DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
-				goto out;
-			}
-			*retlen = written;
-
-			ofs = autoplace ? mtd->oobavail : mtd->oobsize;
-			if (eccbuf)
-				eccbuf += (page - startpage) * ofs;
-			totalpages -= page - startpage;
-			numpages = min (totalpages, ppblock);
-			page &= this->pagemask;
-			startpage = page;
-			oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel,
-					autoplace, numpages);
-			oob = 0;
-			/* Check, if we cross a chip boundary */
-			if (!page) {
-				chipnr++;
-				this->select_chip(mtd, -1);
-				this->select_chip(mtd, chipnr);
-			}
+		chip->ecc.calculate(mtd, p, oob);
+		chip->write_buf(mtd, oob, eccbytes);
+		oob += eccbytes;
+
+		if (chip->ecc.postpad) {
+			chip->write_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
 		}
 		}
 	}
 	}
-	/* Verify the remaining pages */
-cmp:
-	this->data_poi = bufstart;
- 	ret = nand_verify_pages (mtd, this, startpage, totalpages,
-		oobbuf, oobsel, chipnr, (eccbuf != NULL));
-	if (!ret)
-		*retlen = written;
-	else
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
-
-out:
-	/* Deselect and wake up anyone waiting on the device */
-	nand_release_device(mtd);
 
 
-	return ret;
+	/* Calculate remaining oob bytes */
+	i = mtd->oobsize - (oob - chip->oob_poi);
+	if (i)
+		chip->write_buf(mtd, oob, i);
 }
 }
 
 
-
 /**
 /**
- * nand_write_oob - [MTD Interface] NAND write out-of-band
+ * nand_write_page - [INTERNAL] write one page
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
- * @to:		offset to write to
- * @len:	number of bytes to write
- * @retlen:	pointer to variable to store the number of written bytes
+ * @chip:	NAND chip descriptor
  * @buf:	the data to write
  * @buf:	the data to write
- *
- * NAND write out-of-band
+ * @page:	page number to write
+ * @cached:	cached programming
  */
  */
-static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+			   const uint8_t *buf, int page, int cached)
 {
 {
-	int column, page, status, ret = -EIO, chipnr;
-	struct nand_chip *this = mtd->priv;
+	int status;
 
 
-	DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
 
 
-	/* Shift to get page */
-	page = (int) (to >> this->page_shift);
-	chipnr = (int) (to >> this->chip_shift);
+	chip->ecc.write_page(mtd, chip, buf);
 
 
-	/* Mask to get column */
-	column = to & (mtd->oobsize - 1);
+	/*
+	 * 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;
 
 
-	/* Initialize return length value */
-	*retlen = 0;
+	if (!cached || !(chip->options & NAND_CACHEPRG)) {
 
 
-	/* Do not allow write past end of page */
-	if ((column + len) > mtd->oobsize) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
-		return -EINVAL;
+		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);
 	}
 	}
 
 
-	/* Grab the lock and see if the device is available */
-	nand_get_device (this, mtd, FL_WRITING);
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+	/* Send command to read back the data */
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
 
 
-	/* Select the NAND device */
-	this->select_chip(mtd, chipnr);
+	if (chip->verify_buf(mtd, buf, mtd->writesize))
+		return -EIO;
+#endif
+	return 0;
+}
+
+/**
+ * nand_fill_oob - [Internal] Transfer client buffer to oob
+ * @chip:	nand chip structure
+ * @oob:	oob data buffer
+ * @ops:	oob ops structure
+ */
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
+				  struct mtd_oob_ops *ops)
+{
+	size_t len = ops->ooblen;
+
+	switch(ops->mode) {
+
+	case MTD_OOB_PLACE:
+	case MTD_OOB_RAW:
+		memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+		return oob + len;
+
+	case MTD_OOB_AUTO: {
+		struct nand_oobfree *free = chip->ecc.layout->oobfree;
+		uint32_t boffs = 0, woffs = ops->ooboffs;
+		size_t bytes = 0;
+
+		for(; free->length && len; free++, len -= bytes) {
+			/* Write request not from offset 0 ? */
+			if (unlikely(woffs)) {
+				if (woffs >= free->length) {
+					woffs -= free->length;
+					continue;
+				}
+				boffs = free->offset + woffs;
+				bytes = min_t(size_t, len,
+					      (free->length - woffs));
+				woffs = 0;
+			} else {
+				bytes = min_t(size_t, len, free->length);
+				boffs = free->offset;
+			}
+			memcpy(chip->oob_poi + woffs, oob, bytes);
+			oob += bytes;
+		}
+		return oob;
+	}
+	default:
+		BUG();
+	}
+	return NULL;
+}
+
+#define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
+
+/**
+ * nand_do_write_ops - [Internal] NAND write with ECC
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @ops:	oob operations description structure
+ *
+ * NAND write with ECC
+ */
+static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops)
+{
+	int chipnr, realpage, page, blockmask;
+	struct nand_chip *chip = mtd->priv;
+	uint32_t writelen = ops->len;
+	uint8_t *oob = ops->oobbuf;
+	uint8_t *buf = ops->datbuf;
+	int bytes = mtd->writesize;
+	int ret;
+
+	ops->retlen = 0;
 
 
-	/* Reset the chip. Some chips (like the Toshiba TC5832DC found
-	   in one of my DiskOnChip 2000 test units) will clear the whole
-	   data page too if we don't do this. I have no clue why, but
-	   I seem to have 'fixed' it in the doc2000 driver in
-	   August 1999.  dwmw2. */
-	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	/* reject writes, which are not page aligned */
+	if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
+		printk(KERN_NOTICE "nand_write: "
+		       "Attempt to write not page aligned data\n");
+		return -EINVAL;
+	}
+
+	if (!writelen)
+		return 0;
 
 
 	/* Check, if it is write protected */
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd))
 	if (nand_check_wp(mtd))
-		goto out;
+		return -EIO;
 
 
-	/* Invalidate the page cache, if we write to the cached page */
-	if (page == this->pagebuf)
-		this->pagebuf = -1;
-
-	if (NAND_MUST_PAD(this)) {
-		/* Write out desired data */
-		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
-		/* prepad 0xff for partial programming */
-		this->write_buf(mtd, ffchars, column);
-		/* write data */
-		this->write_buf(mtd, buf, len);
-		/* postpad 0xff for partial programming */
-		this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
-	} else {
-		/* Write out desired data */
-		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
-		/* write data */
-		this->write_buf(mtd, buf, len);
-	}
-	/* Send command to program the OOB data */
-	this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+	chipnr = (int)(to >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
 
 
-	status = this->waitfunc (mtd, this, FL_WRITING);
+	realpage = (int)(to >> chip->page_shift);
+	page = realpage & chip->pagemask;
+	blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
 
 
-	/* See if device thinks it succeeded */
-	if (status & NAND_STATUS_FAIL) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
-		ret = -EIO;
-		goto out;
-	}
-	/* Return happy */
-	*retlen = len;
+	/* Invalidate the page cache, when we write to the cached page */
+	if (to <= (chip->pagebuf << chip->page_shift) &&
+	    (chip->pagebuf << chip->page_shift) < (to + ops->len))
+		chip->pagebuf = -1;
 
 
-#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
-	/* Send command to read back the data */
-	this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
+	chip->oob_poi = chip->buffers.oobwbuf;
 
 
-	if (this->verify_buf(mtd, buf, len)) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
-		ret = -EIO;
-		goto out;
+	while(1) {
+		int cached = writelen > bytes && page != blockmask;
+
+		if (unlikely(oob))
+			oob = nand_fill_oob(chip, oob, ops);
+
+		ret = nand_write_page(mtd, chip, buf, page, cached);
+		if (ret)
+			break;
+
+		writelen -= bytes;
+		if (!writelen)
+			break;
+
+		buf += bytes;
+		realpage++;
+
+		page = realpage & chip->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!page) {
+			chipnr++;
+			chip->select_chip(mtd, -1);
+			chip->select_chip(mtd, chipnr);
+		}
 	}
 	}
-#endif
-	ret = 0;
-out:
-	/* Deselect and wake up anyone waiting on the device */
-	nand_release_device(mtd);
 
 
+	if (unlikely(oob))
+		memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+	ops->retlen = ops->len - writelen;
 	return ret;
 	return ret;
 }
 }
 
 
-
 /**
 /**
- * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc
+ * nand_write - [MTD Interface] NAND write with ECC
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
- * @vecs:	the iovectors to write
- * @count:	number of vectors
  * @to:		offset to write to
  * @to:		offset to write to
+ * @len:	number of bytes to write
  * @retlen:	pointer to variable to store the number of written bytes
  * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
  *
  *
- * NAND write with kvec. This just calls the ecc function
+ * NAND write with ECC
  */
  */
-static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count,
-		loff_t to, size_t * retlen)
+static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+			  size_t *retlen, const uint8_t *buf)
 {
 {
-	return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL));
+	struct nand_chip *chip = mtd->priv;
+	int ret;
+
+	/* Do not allow reads past end of device */
+	if ((to + len) > mtd->size)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	nand_get_device(chip, mtd, FL_WRITING);
+
+	chip->ops.len = len;
+	chip->ops.datbuf = (uint8_t *)buf;
+	chip->ops.oobbuf = NULL;
+
+	ret = nand_do_write_ops(mtd, to, &chip->ops);
+
+	nand_release_device(mtd);
+
+	*retlen = chip->ops.retlen;
+	return ret;
 }
 }
 
 
 /**
 /**
- * nand_writev_ecc - [MTD Interface] write with iovec with ecc
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
- * @vecs:	the iovectors to write
- * @count:	number of vectors
  * @to:		offset to write to
  * @to:		offset to write to
- * @retlen:	pointer to variable to store the number of written bytes
- * @eccbuf:	filesystem supplied oob data buffer
- * @oobsel:	oob selection structure
+ * @ops:	oob operation description structure
  *
  *
- * NAND write with iovec with ecc
+ * NAND write out-of-band
  */
  */
-static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count,
-		loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops)
 {
 {
-	int i, page, len, total_len, ret = -EIO, written = 0, chipnr;
-	int oob, numpages, autoplace = 0, startpage;
-	struct nand_chip *this = mtd->priv;
-	int	ppblock = (1 << (this->phys_erase_shift - this->page_shift));
-	u_char *oobbuf, *bufstart;
+	int chipnr, page, status;
+	struct nand_chip *chip = mtd->priv;
 
 
-	/* Preset written len for early exit */
-	*retlen = 0;
-
-	/* Calculate total length of data */
-	total_len = 0;
-	for (i = 0; i < count; i++)
-		total_len += (int) vecs[i].iov_len;
-
-	DEBUG (MTD_DEBUG_LEVEL3,
-	       "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
+	DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
+	      (unsigned int)to, (int)ops->len);
 
 
 	/* Do not allow write past end of page */
 	/* Do not allow write past end of page */
-	if ((to + total_len) > mtd->size) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
+	if ((ops->ooboffs + ops->len) > mtd->oobsize) {
+		DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
+		      "Attempt to write past end of page\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	/* reject writes, which are not page aligned */
-	if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
-		printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
-		return -EINVAL;
-	}
+	chipnr = (int)(to >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
 
 
-	/* Grab the lock and see if the device is available */
-	nand_get_device (this, mtd, FL_WRITING);
+	/* Shift to get page */
+	page = (int)(to >> chip->page_shift);
 
 
-	/* Get the current chip-nr */
-	chipnr = (int) (to >> this->chip_shift);
-	/* Select the NAND device */
-	this->select_chip(mtd, chipnr);
+	/*
+	 * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
+	 * of my DiskOnChip 2000 test units) will clear the whole data page too
+	 * if we don't do this. I have no clue why, but I seem to have 'fixed'
+	 * it in the doc2000 driver in August 1999.  dwmw2.
+	 */
+	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
 
 
 	/* Check, if it is write protected */
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd))
 	if (nand_check_wp(mtd))
-		goto out;
+		return -EROFS;
 
 
-	/* if oobsel is NULL, use chip defaults */
-	if (oobsel == NULL)
-		oobsel = &mtd->oobinfo;
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page == chip->pagebuf)
+		chip->pagebuf = -1;
 
 
-	/* Autoplace of oob data ? Use the default placement scheme */
-	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
-		oobsel = this->autooob;
-		autoplace = 1;
-	}
-	if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
-		autoplace = 1;
+	chip->oob_poi = chip->buffers.oobwbuf;
+	memset(chip->oob_poi, 0xff, mtd->oobsize);
+	nand_fill_oob(chip, ops->oobbuf, ops);
+	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+	memset(chip->oob_poi, 0xff, mtd->oobsize);
 
 
-	/* Setup start page */
-	page = (int) (to >> this->page_shift);
-	/* Invalidate the page cache, if we write to the cached page */
-	if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift))
-		this->pagebuf = -1;
+	if (status)
+		return status;
 
 
-	startpage = page & this->pagemask;
+	ops->retlen = ops->len;
 
 
-	/* Loop until all kvec' data has been written */
-	len = 0;
-	while (count) {
-		/* If the given tuple is >= pagesize then
-		 * write it out from the iov
-		 */
-		if ((vecs->iov_len - len) >= mtd->oobblock) {
-			/* Calc number of pages we can write
-			 * out of this iov in one go */
-			numpages = (vecs->iov_len - len) >> this->page_shift;
-			/* Do not cross block boundaries */
-			numpages = min (ppblock - (startpage & (ppblock - 1)), numpages);
-			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
-			bufstart = (u_char *)vecs->iov_base;
-			bufstart += len;
-			this->data_poi = bufstart;
-			oob = 0;
-			for (i = 1; i <= numpages; i++) {
-				/* Write one page. If this is the last page to write
-				 * then use the real pageprogram command, else select
-				 * cached programming if supported by the chip.
-				 */
-				ret = nand_write_page (mtd, this, page & this->pagemask,
-					&oobbuf[oob], oobsel, i != numpages);
-				if (ret)
-					goto out;
-				this->data_poi += mtd->oobblock;
-				len += mtd->oobblock;
-				oob += mtd->oobsize;
-				page++;
-			}
-			/* Check, if we have to switch to the next tuple */
-			if (len >= (int) vecs->iov_len) {
-				vecs++;
-				len = 0;
-				count--;
-			}
-		} else {
-			/* We must use the internal buffer, read data out of each
-			 * tuple until we have a full page to write
-			 */
-			int cnt = 0;
-			while (cnt < mtd->oobblock) {
-				if (vecs->iov_base != NULL && vecs->iov_len)
-					this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
-				/* Check, if we have to switch to the next tuple */
-				if (len >= (int) vecs->iov_len) {
-					vecs++;
-					len = 0;
-					count--;
-				}
-			}
-			this->pagebuf = page;
-			this->data_poi = this->data_buf;
-			bufstart = this->data_poi;
-			numpages = 1;
-			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
-			ret = nand_write_page (mtd, this, page & this->pagemask,
-				oobbuf, oobsel, 0);
-			if (ret)
-				goto out;
-			page++;
-		}
+	return 0;
+}
 
 
-		this->data_poi = bufstart;
-		ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);
-		if (ret)
-			goto out;
+/**
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @ops:	oob operation description structure
+ */
+static int nand_write_oob(struct mtd_info *mtd, loff_t to,
+			  struct mtd_oob_ops *ops)
+{
+	void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
+			  const uint8_t *buf) = NULL;
+	struct nand_chip *chip = mtd->priv;
+	int ret = -ENOTSUPP;
 
 
-		written += mtd->oobblock * numpages;
-		/* All done ? */
-		if (!count)
-			break;
+	ops->retlen = 0;
 
 
-		startpage = page & this->pagemask;
-		/* Check, if we cross a chip boundary */
-		if (!startpage) {
-			chipnr++;
-			this->select_chip(mtd, -1);
-			this->select_chip(mtd, chipnr);
-		}
+	/* Do not allow writes past end of device */
+	if ((to + ops->len) > mtd->size) {
+		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+		      "Attempt read beyond end of device\n");
+		return -EINVAL;
 	}
 	}
-	ret = 0;
-out:
-	/* Deselect and wake up anyone waiting on the device */
-	nand_release_device(mtd);
 
 
-	*retlen = written;
+	nand_get_device(chip, mtd, FL_WRITING);
+
+	switch(ops->mode) {
+	case MTD_OOB_PLACE:
+	case MTD_OOB_AUTO:
+		break;
+
+	case MTD_OOB_RAW:
+		/* Replace the write_page algorithm temporary */
+		write_page = chip->ecc.write_page;
+		chip->ecc.write_page = nand_write_page_raw;
+		break;
+
+	default:
+		goto out;
+	}
+
+	if (!ops->datbuf)
+		ret = nand_do_write_oob(mtd, to, ops);
+	else
+		ret = nand_do_write_ops(mtd, to, ops);
+
+	if (unlikely(ops->mode == MTD_OOB_RAW))
+		chip->ecc.write_page = write_page;
+ out:
+	nand_release_device(mtd);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -2050,12 +1816,12 @@ out:
  *
  *
  * Standard erase command for NAND chips
  * Standard erase command for NAND chips
  */
  */
-static void single_erase_cmd (struct mtd_info *mtd, int page)
+static void single_erase_cmd(struct mtd_info *mtd, int page)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	/* Send commands to erase a block */
 	/* Send commands to erase a block */
-	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
-	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
 }
 }
 
 
 /**
 /**
@@ -2066,15 +1832,15 @@ static void single_erase_cmd (struct mtd_info *mtd, int page)
  * AND multi block erase command function
  * AND multi block erase command function
  * Erase 4 consecutive blocks
  * Erase 4 consecutive blocks
  */
  */
-static void multi_erase_cmd (struct mtd_info *mtd, int page)
+static void multi_erase_cmd(struct mtd_info *mtd, int page)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	/* Send commands to erase a block */
 	/* Send commands to erase a block */
-	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
-	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
-	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
-	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
-	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
 }
 }
 
 
 /**
 /**
@@ -2084,79 +1850,82 @@ static void multi_erase_cmd (struct mtd_info *mtd, int page)
  *
  *
  * Erase one ore more blocks
  * Erase one ore more blocks
  */
  */
-static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
+static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
 {
-	return nand_erase_nand (mtd, instr, 0);
+	return nand_erase_nand(mtd, instr, 0);
 }
 }
 
 
 #define BBT_PAGE_MASK	0xffffff3f
 #define BBT_PAGE_MASK	0xffffff3f
 /**
 /**
- * nand_erase_intern - [NAND Interface] erase block(s)
+ * nand_erase_nand - [Internal] erase block(s)
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
  * @instr:	erase instruction
  * @instr:	erase instruction
  * @allowbbt:	allow erasing the bbt area
  * @allowbbt:	allow erasing the bbt area
  *
  *
  * Erase one ore more blocks
  * Erase one ore more blocks
  */
  */
-int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
+int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+		    int allowbbt)
 {
 {
 	int page, len, status, pages_per_block, ret, chipnr;
 	int page, len, status, pages_per_block, ret, chipnr;
-	struct nand_chip *this = mtd->priv;
-	int rewrite_bbt[NAND_MAX_CHIPS]={0};	/* flags to indicate the page, if bbt needs to be rewritten. */
-	unsigned int bbt_masked_page;		/* bbt mask to compare to page being erased. */
-						/* It is used to see if the current page is in the same */
-						/*   256 block group and the same bank as the bbt. */
+	struct nand_chip *chip = mtd->priv;
+	int rewrite_bbt[NAND_MAX_CHIPS]={0};
+	unsigned int bbt_masked_page = 0xffffffff;
 
 
-	DEBUG (MTD_DEBUG_LEVEL3,
-	       "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
+	DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n",
+	      (unsigned int)instr->addr, (unsigned int)instr->len);
 
 
 	/* Start address must align on block boundary */
 	/* Start address must align on block boundary */
-	if (instr->addr & ((1 << this->phys_erase_shift) - 1)) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+	if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
+		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
 	/* Length must align on block boundary */
 	/* Length must align on block boundary */
-	if (instr->len & ((1 << this->phys_erase_shift) - 1)) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
+	if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
+		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+		      "Length not block aligned\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
 	/* Do not allow erase past end of device */
 	/* Do not allow erase past end of device */
 	if ((instr->len + instr->addr) > mtd->size) {
 	if ((instr->len + instr->addr) > mtd->size) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
+		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+		      "Erase past end of device\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
 	instr->fail_addr = 0xffffffff;
 	instr->fail_addr = 0xffffffff;
 
 
 	/* Grab the lock and see if the device is available */
 	/* Grab the lock and see if the device is available */
-	nand_get_device (this, mtd, FL_ERASING);
+	nand_get_device(chip, mtd, FL_ERASING);
 
 
 	/* Shift to get first page */
 	/* Shift to get first page */
-	page = (int) (instr->addr >> this->page_shift);
-	chipnr = (int) (instr->addr >> this->chip_shift);
+	page = (int)(instr->addr >> chip->page_shift);
+	chipnr = (int)(instr->addr >> chip->chip_shift);
 
 
 	/* Calculate pages in each block */
 	/* Calculate pages in each block */
-	pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
+	pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
 
 
 	/* Select the NAND device */
 	/* Select the NAND device */
-	this->select_chip(mtd, chipnr);
+	chip->select_chip(mtd, chipnr);
 
 
-	/* Check the WP bit */
 	/* Check, if it is write protected */
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd)) {
 	if (nand_check_wp(mtd)) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
+		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+		      "Device is write protected!!!\n");
 		instr->state = MTD_ERASE_FAILED;
 		instr->state = MTD_ERASE_FAILED;
 		goto erase_exit;
 		goto erase_exit;
 	}
 	}
 
 
-	/* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */
-	if (this->options & BBT_AUTO_REFRESH) {
-		bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
-	} else {
-		bbt_masked_page = 0xffffffff;	/* should not match anything */
-	}
+	/*
+	 * If BBT requires refresh, set the BBT page mask to see if the BBT
+	 * should be rewritten. Otherwise the mask is set to 0xffffffff which
+	 * can not be matched. This is also done when the bbt is actually
+	 * erased to avoid recusrsive updates
+	 */
+	if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
+		bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
 
 
 	/* Loop through the pages */
 	/* Loop through the pages */
 	len = instr->len;
 	len = instr->len;
@@ -2164,64 +1933,77 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
 	instr->state = MTD_ERASING;
 	instr->state = MTD_ERASING;
 
 
 	while (len) {
 	while (len) {
-		/* Check if we have a bad block, we do not erase bad blocks ! */
-		if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
-			printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
+		/*
+		 * heck if we have a bad block, we do not erase bad blocks !
+		 */
+		if (nand_block_checkbad(mtd, ((loff_t) page) <<
+					chip->page_shift, 0, allowbbt)) {
+			printk(KERN_WARNING "nand_erase: attempt to erase a "
+			       "bad block at page 0x%08x\n", page);
 			instr->state = MTD_ERASE_FAILED;
 			instr->state = MTD_ERASE_FAILED;
 			goto erase_exit;
 			goto erase_exit;
 		}
 		}
 
 
-		/* Invalidate the page cache, if we erase the block which contains
-		   the current cached page */
-		if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
-			this->pagebuf = -1;
+		/*
+		 * Invalidate the page cache, if we erase the block which
+		 * contains the current cached page
+		 */
+		if (page <= chip->pagebuf && chip->pagebuf <
+		    (page + pages_per_block))
+			chip->pagebuf = -1;
 
 
-		this->erase_cmd (mtd, page & this->pagemask);
+		chip->erase_cmd(mtd, page & chip->pagemask);
 
 
-		status = this->waitfunc (mtd, this, FL_ERASING);
+		status = chip->waitfunc(mtd, chip);
 
 
-		/* See if operation failed and additional status checks are available */
-		if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
-			status = this->errstat(mtd, this, FL_ERASING, status, page);
-		}
+		/*
+		 * See if operation failed and additional status checks are
+		 * available
+		 */
+		if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+			status = chip->errstat(mtd, chip, FL_ERASING,
+					       status, page);
 
 
 		/* See if block erase succeeded */
 		/* See if block erase succeeded */
 		if (status & NAND_STATUS_FAIL) {
 		if (status & NAND_STATUS_FAIL) {
-			DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
+			DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+			      "Failed erase, page 0x%08x\n", page);
 			instr->state = MTD_ERASE_FAILED;
 			instr->state = MTD_ERASE_FAILED;
-			instr->fail_addr = (page << this->page_shift);
+			instr->fail_addr = (page << chip->page_shift);
 			goto erase_exit;
 			goto erase_exit;
 		}
 		}
 
 
-		/* if BBT requires refresh, set the BBT rewrite flag to the page being erased */
-		if (this->options & BBT_AUTO_REFRESH) {
-			if (((page & BBT_PAGE_MASK) == bbt_masked_page) &&
-			     (page != this->bbt_td->pages[chipnr])) {
-				rewrite_bbt[chipnr] = (page << this->page_shift);
-			}
-		}
+		/*
+		 * If BBT requires refresh, set the BBT rewrite flag to the
+		 * page being erased
+		 */
+		if (bbt_masked_page != 0xffffffff &&
+		    (page & BBT_PAGE_MASK) == bbt_masked_page)
+			    rewrite_bbt[chipnr] = (page << chip->page_shift);
 
 
 		/* Increment page address and decrement length */
 		/* Increment page address and decrement length */
-		len -= (1 << this->phys_erase_shift);
+		len -= (1 << chip->phys_erase_shift);
 		page += pages_per_block;
 		page += pages_per_block;
 
 
 		/* Check, if we cross a chip boundary */
 		/* Check, if we cross a chip boundary */
-		if (len && !(page & this->pagemask)) {
+		if (len && !(page & chip->pagemask)) {
 			chipnr++;
 			chipnr++;
-			this->select_chip(mtd, -1);
-			this->select_chip(mtd, chipnr);
-
-			/* if BBT requires refresh and BBT-PERCHIP,
-			 *   set the BBT page mask to see if this BBT should be rewritten */
-			if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) {
-				bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
-			}
+			chip->select_chip(mtd, -1);
+			chip->select_chip(mtd, chipnr);
 
 
+			/*
+			 * If BBT requires refresh and BBT-PERCHIP, set the BBT
+			 * page mask to see if this BBT should be rewritten
+			 */
+			if (bbt_masked_page != 0xffffffff &&
+			    (chip->bbt_td->options & NAND_BBT_PERCHIP))
+				bbt_masked_page = chip->bbt_td->pages[chipnr] &
+					BBT_PAGE_MASK;
 		}
 		}
 	}
 	}
 	instr->state = MTD_ERASE_DONE;
 	instr->state = MTD_ERASE_DONE;
 
 
-erase_exit:
+ erase_exit:
 
 
 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
 	/* Do call back function */
 	/* Do call back function */
@@ -2231,16 +2013,21 @@ erase_exit:
 	/* Deselect and wake up anyone waiting on the device */
 	/* Deselect and wake up anyone waiting on the device */
 	nand_release_device(mtd);
 	nand_release_device(mtd);
 
 
-	/* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */
-	if ((this->options & BBT_AUTO_REFRESH) && (!ret)) {
-		for (chipnr = 0; chipnr < this->numchips; chipnr++) {
-			if (rewrite_bbt[chipnr]) {
-				/* update the BBT for chip */
-				DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n",
-					chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]);
-				nand_update_bbt (mtd, rewrite_bbt[chipnr]);
-			}
-		}
+	/*
+	 * If BBT requires refresh and erase was successful, rewrite any
+	 * selected bad block tables
+	 */
+	if (bbt_masked_page == 0xffffffff || ret)
+		return ret;
+
+	for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
+		if (!rewrite_bbt[chipnr])
+			continue;
+		/* update the BBT for chip */
+		DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
+		      "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
+		      chip->bbt_td->pages[chipnr]);
+		nand_update_bbt(mtd, rewrite_bbt[chipnr]);
 	}
 	}
 
 
 	/* Return more or less happy */
 	/* Return more or less happy */
@@ -2253,51 +2040,50 @@ erase_exit:
  *
  *
  * Sync is actually a wait for chip ready function
  * Sync is actually a wait for chip ready function
  */
  */
-static void nand_sync (struct mtd_info *mtd)
+static void nand_sync(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
-	DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+	DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n");
 
 
 	/* Grab the lock and see if the device is available */
 	/* Grab the lock and see if the device is available */
-	nand_get_device (this, mtd, FL_SYNCING);
+	nand_get_device(chip, mtd, FL_SYNCING);
 	/* Release it and go back */
 	/* Release it and go back */
-	nand_release_device (mtd);
+	nand_release_device(mtd);
 }
 }
 
 
-
 /**
 /**
- * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
+ * nand_block_isbad - [MTD Interface] Check if block at offset is bad
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
  * @ofs:	offset relative to mtd start
  * @ofs:	offset relative to mtd start
  */
  */
-static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
+static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
 {
 {
 	/* Check for invalid offset */
 	/* Check for invalid offset */
-	if (ofs > mtd->size)
+	if (offs > mtd->size)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	return nand_block_checkbad (mtd, ofs, 1, 0);
+	return nand_block_checkbad(mtd, offs, 1, 0);
 }
 }
 
 
 /**
 /**
- * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
+ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
  * @ofs:	offset relative to mtd start
  * @ofs:	offset relative to mtd start
  */
  */
-static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
+static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	int ret;
 	int ret;
 
 
-        if ((ret = nand_block_isbad(mtd, ofs))) {
-        	/* If it was bad already, return success and do nothing. */
+	if ((ret = nand_block_isbad(mtd, ofs))) {
+		/* If it was bad already, return success and do nothing. */
 		if (ret > 0)
 		if (ret > 0)
 			return 0;
 			return 0;
-        	return ret;
-        }
+		return ret;
+	}
 
 
-	return this->block_markbad(mtd, ofs);
+	return chip->block_markbad(mtd, ofs);
 }
 }
 
 
 /**
 /**
@@ -2306,9 +2092,9 @@ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
  */
  */
 static int nand_suspend(struct mtd_info *mtd)
 static int nand_suspend(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
-	return nand_get_device (this, mtd, FL_PM_SUSPENDED);
+	return nand_get_device(chip, mtd, FL_PM_SUSPENDED);
 }
 }
 
 
 /**
 /**
@@ -2317,373 +2103,385 @@ static int nand_suspend(struct mtd_info *mtd)
  */
  */
 static void nand_resume(struct mtd_info *mtd)
 static void nand_resume(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
-	if (this->state == FL_PM_SUSPENDED)
+	if (chip->state == FL_PM_SUSPENDED)
 		nand_release_device(mtd);
 		nand_release_device(mtd);
 	else
 	else
-		printk(KERN_ERR "resume() called for the chip which is not "
-				"in suspended state\n");
-
+		printk(KERN_ERR "nand_resume() called for a chip which is not "
+		       "in suspended state\n");
 }
 }
 
 
-
-/**
- * nand_scan - [NAND Interface] Scan for the NAND device
- * @mtd:	MTD device structure
- * @maxchips:	Number of chips to scan for
- *
- * This fills out all the not initialized function pointers
- * with the defaults.
- * The flash ID is read and the mtd/chip structures are
- * filled with the appropriate values. Buffers are allocated if
- * they are not provided by the board driver
- *
+/*
+ * Set default functions
  */
  */
-int nand_scan (struct mtd_info *mtd, int maxchips)
+static void nand_set_defaults(struct nand_chip *chip, int busw)
 {
 {
-	int i, nand_maf_id, nand_dev_id, busw, maf_id;
-	struct nand_chip *this = mtd->priv;
-
-	/* Get buswidth to select the correct functions*/
-	busw = this->options & NAND_BUSWIDTH_16;
-
 	/* check for proper chip_delay setup, set 20us if not */
 	/* check for proper chip_delay setup, set 20us if not */
-	if (!this->chip_delay)
-		this->chip_delay = 20;
+	if (!chip->chip_delay)
+		chip->chip_delay = 20;
 
 
 	/* check, if a user supplied command function given */
 	/* check, if a user supplied command function given */
-	if (this->cmdfunc == NULL)
-		this->cmdfunc = nand_command;
+	if (chip->cmdfunc == NULL)
+		chip->cmdfunc = nand_command;
 
 
 	/* check, if a user supplied wait function given */
 	/* check, if a user supplied wait function given */
-	if (this->waitfunc == NULL)
-		this->waitfunc = nand_wait;
-
-	if (!this->select_chip)
-		this->select_chip = nand_select_chip;
-	if (!this->write_byte)
-		this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
-	if (!this->read_byte)
-		this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
-	if (!this->write_word)
-		this->write_word = nand_write_word;
-	if (!this->read_word)
-		this->read_word = nand_read_word;
-	if (!this->block_bad)
-		this->block_bad = nand_block_bad;
-	if (!this->block_markbad)
-		this->block_markbad = nand_default_block_markbad;
-	if (!this->write_buf)
-		this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
-	if (!this->read_buf)
-		this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
-	if (!this->verify_buf)
-		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
-	if (!this->scan_bbt)
-		this->scan_bbt = nand_default_bbt;
+	if (chip->waitfunc == NULL)
+		chip->waitfunc = nand_wait;
+
+	if (!chip->select_chip)
+		chip->select_chip = nand_select_chip;
+	if (!chip->read_byte)
+		chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
+	if (!chip->read_word)
+		chip->read_word = nand_read_word;
+	if (!chip->block_bad)
+		chip->block_bad = nand_block_bad;
+	if (!chip->block_markbad)
+		chip->block_markbad = nand_default_block_markbad;
+	if (!chip->write_buf)
+		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+	if (!chip->read_buf)
+		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+	if (!chip->verify_buf)
+		chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+	if (!chip->scan_bbt)
+		chip->scan_bbt = nand_default_bbt;
+
+	if (!chip->controller) {
+		chip->controller = &chip->hwcontrol;
+		spin_lock_init(&chip->controller->lock);
+		init_waitqueue_head(&chip->controller->wq);
+	}
+
+}
+
+/*
+ * Get the flash and manufacturer id and lookup if the type is supported
+ */
+static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
+						  struct nand_chip *chip,
+						  int busw, int *maf_id)
+{
+	struct nand_flash_dev *type = NULL;
+	int i, dev_id, maf_idx;
 
 
 	/* Select the device */
 	/* Select the device */
-	this->select_chip(mtd, 0);
+	chip->select_chip(mtd, 0);
 
 
 	/* Send the command for reading device ID */
 	/* Send the command for reading device ID */
-	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 
 
 	/* Read manufacturer and device IDs */
 	/* Read manufacturer and device IDs */
-	nand_maf_id = this->read_byte(mtd);
-	nand_dev_id = this->read_byte(mtd);
+	*maf_id = chip->read_byte(mtd);
+	dev_id = chip->read_byte(mtd);
 
 
-	/* Print and store flash device information */
+	/* Lookup the flash id */
 	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
 	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+		if (dev_id == nand_flash_ids[i].id) {
+			type =  &nand_flash_ids[i];
+			break;
+		}
+	}
 
 
-		if (nand_dev_id != nand_flash_ids[i].id)
-			continue;
+	if (!type)
+		return ERR_PTR(-ENODEV);
+
+	if (!mtd->name)
+		mtd->name = type->name;
+
+	chip->chipsize = type->chipsize << 20;
+
+	/* Newer devices have all the information in additional id bytes */
+	if (!type->pagesize) {
+		int extid;
+		/* The 3rd id byte contains non relevant data ATM */
+		extid = chip->read_byte(mtd);
+		/* The 4th id byte is the important one */
+		extid = chip->read_byte(mtd);
+		/* Calc pagesize */
+		mtd->writesize = 1024 << (extid & 0x3);
+		extid >>= 2;
+		/* Calc oobsize */
+		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+		extid >>= 2;
+		/* Calc blocksize. Blocksize is multiples of 64KiB */
+		mtd->erasesize = (64 * 1024) << (extid & 0x03);
+		extid >>= 2;
+		/* Get buswidth information */
+		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
 
 
-		if (!mtd->name) mtd->name = nand_flash_ids[i].name;
-		this->chipsize = nand_flash_ids[i].chipsize << 20;
-
-		/* New devices have all the information in additional id bytes */
-		if (!nand_flash_ids[i].pagesize) {
-			int extid;
-			/* The 3rd id byte contains non relevant data ATM */
-			extid = this->read_byte(mtd);
-			/* The 4th id byte is the important one */
-			extid = this->read_byte(mtd);
-			/* Calc pagesize */
-			mtd->oobblock = 1024 << (extid & 0x3);
-			extid >>= 2;
-			/* Calc oobsize */
-			mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock >> 9);
-			extid >>= 2;
-			/* Calc blocksize. Blocksize is multiples of 64KiB */
-			mtd->erasesize = (64 * 1024)  << (extid & 0x03);
-			extid >>= 2;
-			/* Get buswidth information */
-			busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+	} else {
+		/*
+		 * Old devices have chip data hardcoded in the device id table
+		 */
+		mtd->erasesize = type->erasesize;
+		mtd->writesize = type->pagesize;
+		mtd->oobsize = mtd->writesize / 32;
+		busw = type->options & NAND_BUSWIDTH_16;
+	}
 
 
-		} else {
-			/* Old devices have this data hardcoded in the
-			 * device id table */
-			mtd->erasesize = nand_flash_ids[i].erasesize;
-			mtd->oobblock = nand_flash_ids[i].pagesize;
-			mtd->oobsize = mtd->oobblock / 32;
-			busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
-		}
+	/* Try to identify manufacturer */
+	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_id++) {
+		if (nand_manuf_ids[maf_idx].id == *maf_id)
+			break;
+	}
 
 
-		/* Try to identify manufacturer */
-		for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) {
-			if (nand_manuf_ids[maf_id].id == nand_maf_id)
-				break;
-		}
+	/*
+	 * Check, if buswidth is correct. Hardware drivers should set
+	 * chip correct !
+	 */
+	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
+		printk(KERN_INFO "NAND device: Manufacturer ID:"
+		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
+		       dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
+		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
+		       busw ? 16 : 8);
+		return ERR_PTR(-EINVAL);
+	}
 
 
-		/* Check, if buswidth is correct. Hardware drivers should set
-		 * this correct ! */
-		if (busw != (this->options & NAND_BUSWIDTH_16)) {
-			printk (KERN_INFO "NAND device: Manufacturer ID:"
-				" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
-				nand_manuf_ids[maf_id].name , mtd->name);
-			printk (KERN_WARNING
-				"NAND bus width %d instead %d bit\n",
-					(this->options & NAND_BUSWIDTH_16) ? 16 : 8,
-					busw ? 16 : 8);
-			this->select_chip(mtd, -1);
-			return 1;
-		}
+	/* Calculate the address shift from the page size */
+	chip->page_shift = ffs(mtd->writesize) - 1;
+	/* Convert chipsize to number of pages per chip -1. */
+	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
 
 
-		/* Calculate the address shift from the page size */
-		this->page_shift = ffs(mtd->oobblock) - 1;
-		this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
-		this->chip_shift = ffs(this->chipsize) - 1;
-
-		/* Set the bad block position */
-		this->badblockpos = mtd->oobblock > 512 ?
-			NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
-
-		/* Get chip options, preserve non chip based options */
-		this->options &= ~NAND_CHIPOPTIONS_MSK;
-		this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
-		/* Set this as a default. Board drivers can override it, if neccecary */
-		this->options |= NAND_NO_AUTOINCR;
-		/* Check if this is a not a samsung device. Do not clear the options
-		 * for chips which are not having an extended id.
-		 */
-		if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
-			this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+	chip->bbt_erase_shift = chip->phys_erase_shift =
+		ffs(mtd->erasesize) - 1;
+	chip->chip_shift = ffs(chip->chipsize) - 1;
 
 
-		/* Check for AND chips with 4 page planes */
-		if (this->options & NAND_4PAGE_ARRAY)
-			this->erase_cmd = multi_erase_cmd;
-		else
-			this->erase_cmd = single_erase_cmd;
+	/* Set the bad block position */
+	chip->badblockpos = mtd->writesize > 512 ?
+		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
 
 
-		/* Do not replace user supplied command function ! */
-		if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
-			this->cmdfunc = nand_command_lp;
+	/* Get chip options, preserve non chip based options */
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
 
 
-		printk (KERN_INFO "NAND device: Manufacturer ID:"
-			" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
-			nand_manuf_ids[maf_id].name , nand_flash_ids[i].name);
-		break;
-	}
+	/*
+	 * Set chip as a default. Board drivers can override it, if necessary
+	 */
+	chip->options |= NAND_NO_AUTOINCR;
+
+	/* Check if chip is a not a samsung device. Do not clear the
+	 * options for chips which are not having an extended id.
+	 */
+	if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
+		chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+
+	/* Check for AND chips with 4 page planes */
+	if (chip->options & NAND_4PAGE_ARRAY)
+		chip->erase_cmd = multi_erase_cmd;
+	else
+		chip->erase_cmd = single_erase_cmd;
+
+	/* Do not replace user supplied command function ! */
+	if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
+		chip->cmdfunc = nand_command_lp;
+
+	printk(KERN_INFO "NAND device: Manufacturer ID:"
+	       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
+	       nand_manuf_ids[maf_idx].name, type->name);
+
+	return type;
+}
+
+/* module_text_address() isn't exported, and it's mostly a pointless
+   test if this is a module _anyway_ -- they'd have to try _really_ hard
+   to call us from in-kernel code if the core NAND support is modular. */
+#ifdef MODULE
+#define caller_is_module() (1)
+#else
+#define caller_is_module() \
+	module_text_address((unsigned long)__builtin_return_address(0))
+#endif
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd:	MTD device structure
+ * @maxchips:	Number of chips to scan for
+ *
+ * This fills out all the uninitialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values.
+ * The mtd->owner field must be set to the module of the caller
+ *
+ */
+int nand_scan(struct mtd_info *mtd, int maxchips)
+{
+	int i, busw, nand_maf_id;
+	struct nand_chip *chip = mtd->priv;
+	struct nand_flash_dev *type;
 
 
-	if (!nand_flash_ids[i].name) {
-		printk (KERN_WARNING "No NAND device found!!!\n");
-		this->select_chip(mtd, -1);
-		return 1;
+	/* Many callers got this wrong, so check for it for a while... */
+	if (!mtd->owner && caller_is_module()) {
+		printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
+		BUG();
 	}
 	}
 
 
-	for (i=1; i < maxchips; i++) {
-		this->select_chip(mtd, i);
+	/* Get buswidth to select the correct functions */
+	busw = chip->options & NAND_BUSWIDTH_16;
+	/* Set the default functions */
+	nand_set_defaults(chip, busw);
 
 
-		/* Send the command for reading device ID */
-		this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+	/* Read the flash type */
+	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
+
+	if (IS_ERR(type)) {
+		printk(KERN_WARNING "No NAND device found!!!\n");
+		chip->select_chip(mtd, -1);
+		return PTR_ERR(type);
+	}
 
 
+	/* Check for a chip array */
+	for (i = 1; i < maxchips; i++) {
+		chip->select_chip(mtd, i);
+		/* Send the command for reading device ID */
+		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 		/* Read manufacturer and device IDs */
 		/* Read manufacturer and device IDs */
-		if (nand_maf_id != this->read_byte(mtd) ||
-		    nand_dev_id != this->read_byte(mtd))
+		if (nand_maf_id != chip->read_byte(mtd) ||
+		    type->id != chip->read_byte(mtd))
 			break;
 			break;
 	}
 	}
 	if (i > 1)
 	if (i > 1)
 		printk(KERN_INFO "%d NAND chips detected\n", i);
 		printk(KERN_INFO "%d NAND chips detected\n", i);
 
 
-	/* Allocate buffers, if neccecary */
-	if (!this->oob_buf) {
-		size_t len;
-		len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
-		this->oob_buf = kmalloc (len, GFP_KERNEL);
-		if (!this->oob_buf) {
-			printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
-			return -ENOMEM;
-		}
-		this->options |= NAND_OOBBUF_ALLOC;
-	}
+	/* Store the number of chips and calc total size for mtd */
+	chip->numchips = i;
+	mtd->size = i * chip->chipsize;
 
 
-	if (!this->data_buf) {
-		size_t len;
-		len = mtd->oobblock + mtd->oobsize;
-		this->data_buf = kmalloc (len, GFP_KERNEL);
-		if (!this->data_buf) {
-			if (this->options & NAND_OOBBUF_ALLOC)
-				kfree (this->oob_buf);
-			printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
-			return -ENOMEM;
-		}
-		this->options |= NAND_DATABUF_ALLOC;
-	}
+	/* Preset the internal oob write buffer */
+	memset(chip->buffers.oobwbuf, 0xff, mtd->oobsize);
 
 
-	/* Store the number of chips and calc total size for mtd */
-	this->numchips = i;
-	mtd->size = i * this->chipsize;
-	/* Convert chipsize to number of pages per chip -1. */
-	this->pagemask = (this->chipsize >> this->page_shift) - 1;
-	/* Preset the internal oob buffer */
-	memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
-
-	/* If no default placement scheme is given, select an
-	 * appropriate one */
-	if (!this->autooob) {
-		/* Select the appropriate default oob placement scheme for
-		 * placement agnostic filesystems */
+	/*
+	 * If no default placement scheme is given, select an appropriate one
+	 */
+	if (!chip->ecc.layout) {
 		switch (mtd->oobsize) {
 		switch (mtd->oobsize) {
 		case 8:
 		case 8:
-			this->autooob = &nand_oob_8;
+			chip->ecc.layout = &nand_oob_8;
 			break;
 			break;
 		case 16:
 		case 16:
-			this->autooob = &nand_oob_16;
+			chip->ecc.layout = &nand_oob_16;
 			break;
 			break;
 		case 64:
 		case 64:
-			this->autooob = &nand_oob_64;
+			chip->ecc.layout = &nand_oob_64;
 			break;
 			break;
 		default:
 		default:
-			printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
-				mtd->oobsize);
+			printk(KERN_WARNING "No oob scheme defined for "
+			       "oobsize %d\n", mtd->oobsize);
 			BUG();
 			BUG();
 		}
 		}
 	}
 	}
 
 
-	/* The number of bytes available for the filesystem to place fs dependend
-	 * oob data */
-	mtd->oobavail = 0;
-	for (i = 0; this->autooob->oobfree[i][1]; i++)
-		mtd->oobavail += this->autooob->oobfree[i][1];
-
 	/*
 	/*
-	 * check ECC mode, default to software
-	 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
-	 * fallback to software ECC
-	*/
-	this->eccsize = 256;	/* set default eccsize */
-	this->eccbytes = 3;
-
-	switch (this->eccmode) {
-	case NAND_ECC_HW12_2048:
-		if (mtd->oobblock < 2048) {
-			printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
-			       mtd->oobblock);
-			this->eccmode = NAND_ECC_SOFT;
-			this->calculate_ecc = nand_calculate_ecc;
-			this->correct_data = nand_correct_data;
-		} else
-			this->eccsize = 2048;
-		break;
-
-	case NAND_ECC_HW3_512:
-	case NAND_ECC_HW6_512:
-	case NAND_ECC_HW8_512:
-		if (mtd->oobblock == 256) {
-			printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
-			this->eccmode = NAND_ECC_SOFT;
-			this->calculate_ecc = nand_calculate_ecc;
-			this->correct_data = nand_correct_data;
-		} else
-			this->eccsize = 512; /* set eccsize to 512 */
-		break;
+	 * check ECC mode, default to software if 3byte/512byte hardware ECC is
+	 * selected and we have 256 byte pagesize fallback to software ECC
+	 */
+	switch (chip->ecc.mode) {
+	case NAND_ECC_HW:
+		/* Use standard hwecc read page function ? */
+		if (!chip->ecc.read_page)
+			chip->ecc.read_page = nand_read_page_hwecc;
+		if (!chip->ecc.write_page)
+			chip->ecc.write_page = nand_write_page_hwecc;
+		if (!chip->ecc.read_oob)
+			chip->ecc.read_oob = nand_read_oob_std;
+		if (!chip->ecc.write_oob)
+			chip->ecc.write_oob = nand_write_oob_std;
+
+	case NAND_ECC_HW_SYNDROME:
+		if (!chip->ecc.calculate || !chip->ecc.correct ||
+		    !chip->ecc.hwctl) {
+			printk(KERN_WARNING "No ECC functions supplied, "
+			       "Hardware ECC not possible\n");
+			BUG();
+		}
+		/* Use standard syndrome read/write page function ? */
+		if (!chip->ecc.read_page)
+			chip->ecc.read_page = nand_read_page_syndrome;
+		if (!chip->ecc.write_page)
+			chip->ecc.write_page = nand_write_page_syndrome;
+		if (!chip->ecc.read_oob)
+			chip->ecc.read_oob = nand_read_oob_syndrome;
+		if (!chip->ecc.write_oob)
+			chip->ecc.write_oob = nand_write_oob_syndrome;
+
+		if (mtd->writesize >= chip->ecc.size)
+			break;
+		printk(KERN_WARNING "%d byte HW ECC not possible on "
+		       "%d byte page size, fallback to SW ECC\n",
+		       chip->ecc.size, mtd->writesize);
+		chip->ecc.mode = NAND_ECC_SOFT;
 
 
-	case NAND_ECC_HW3_256:
+	case NAND_ECC_SOFT:
+		chip->ecc.calculate = nand_calculate_ecc;
+		chip->ecc.correct = nand_correct_data;
+		chip->ecc.read_page = nand_read_page_swecc;
+		chip->ecc.write_page = nand_write_page_swecc;
+		chip->ecc.read_oob = nand_read_oob_std;
+		chip->ecc.write_oob = nand_write_oob_std;
+		chip->ecc.size = 256;
+		chip->ecc.bytes = 3;
 		break;
 		break;
 
 
 	case NAND_ECC_NONE:
 	case NAND_ECC_NONE:
-		printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
-		this->eccmode = NAND_ECC_NONE;
-		break;
-
-	case NAND_ECC_SOFT:
-		this->calculate_ecc = nand_calculate_ecc;
-		this->correct_data = nand_correct_data;
+		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
+		       "This is not recommended !!\n");
+		chip->ecc.read_page = nand_read_page_raw;
+		chip->ecc.write_page = nand_write_page_raw;
+		chip->ecc.read_oob = nand_read_oob_std;
+		chip->ecc.write_oob = nand_write_oob_std;
+		chip->ecc.size = mtd->writesize;
+		chip->ecc.bytes = 0;
 		break;
 		break;
-
 	default:
 	default:
-		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+		printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
+		       chip->ecc.mode);
 		BUG();
 		BUG();
 	}
 	}
 
 
-	/* Check hardware ecc function availability and adjust number of ecc bytes per
-	 * calculation step
-	*/
-	switch (this->eccmode) {
-	case NAND_ECC_HW12_2048:
-		this->eccbytes += 4;
-	case NAND_ECC_HW8_512:
-		this->eccbytes += 2;
-	case NAND_ECC_HW6_512:
-		this->eccbytes += 3;
-	case NAND_ECC_HW3_512:
-	case NAND_ECC_HW3_256:
-		if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
-			break;
-		printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
-		BUG();
-	}
-
-	mtd->eccsize = this->eccsize;
-
-	/* Set the number of read / write steps for one page to ensure ECC generation */
-	switch (this->eccmode) {
-	case NAND_ECC_HW12_2048:
-		this->eccsteps = mtd->oobblock / 2048;
-		break;
-	case NAND_ECC_HW3_512:
-	case NAND_ECC_HW6_512:
-	case NAND_ECC_HW8_512:
-		this->eccsteps = mtd->oobblock / 512;
-		break;
-	case NAND_ECC_HW3_256:
-	case NAND_ECC_SOFT:
-		this->eccsteps = mtd->oobblock / 256;
-		break;
+	/*
+	 * The number of bytes available for a client to place data into
+	 * the out of band area
+	 */
+	chip->ecc.layout->oobavail = 0;
+	for (i = 0; chip->ecc.layout->oobfree[i].length; i++)
+		chip->ecc.layout->oobavail +=
+			chip->ecc.layout->oobfree[i].length;
 
 
-	case NAND_ECC_NONE:
-		this->eccsteps = 1;
-		break;
+	/*
+	 * Set the number of read / write steps for one page depending on ECC
+	 * mode
+	 */
+	chip->ecc.steps = mtd->writesize / chip->ecc.size;
+	if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
+		printk(KERN_WARNING "Invalid ecc parameters\n");
+		BUG();
 	}
 	}
+	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
 
 
-	/* Initialize state, waitqueue and spinlock */
-	this->state = FL_READY;
-	init_waitqueue_head (&this->wq);
-	spin_lock_init (&this->chip_lock);
+	/* Initialize state */
+	chip->state = FL_READY;
 
 
 	/* De-select the device */
 	/* De-select the device */
-	this->select_chip(mtd, -1);
+	chip->select_chip(mtd, -1);
 
 
 	/* Invalidate the pagebuffer reference */
 	/* Invalidate the pagebuffer reference */
-	this->pagebuf = -1;
+	chip->pagebuf = -1;
 
 
 	/* Fill in remaining MTD driver data */
 	/* Fill in remaining MTD driver data */
 	mtd->type = MTD_NANDFLASH;
 	mtd->type = MTD_NANDFLASH;
-	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+	mtd->flags = MTD_CAP_NANDFLASH;
 	mtd->ecctype = MTD_ECC_SW;
 	mtd->ecctype = MTD_ECC_SW;
 	mtd->erase = nand_erase;
 	mtd->erase = nand_erase;
 	mtd->point = NULL;
 	mtd->point = NULL;
 	mtd->unpoint = NULL;
 	mtd->unpoint = NULL;
 	mtd->read = nand_read;
 	mtd->read = nand_read;
 	mtd->write = nand_write;
 	mtd->write = nand_write;
-	mtd->read_ecc = nand_read_ecc;
-	mtd->write_ecc = nand_write_ecc;
 	mtd->read_oob = nand_read_oob;
 	mtd->read_oob = nand_read_oob;
 	mtd->write_oob = nand_write_oob;
 	mtd->write_oob = nand_write_oob;
-	mtd->readv = NULL;
-	mtd->writev = nand_writev;
-	mtd->writev_ecc = nand_writev_ecc;
 	mtd->sync = nand_sync;
 	mtd->sync = nand_sync;
 	mtd->lock = NULL;
 	mtd->lock = NULL;
 	mtd->unlock = NULL;
 	mtd->unlock = NULL;
@@ -2692,47 +2490,38 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
 	mtd->block_isbad = nand_block_isbad;
 	mtd->block_isbad = nand_block_isbad;
 	mtd->block_markbad = nand_block_markbad;
 	mtd->block_markbad = nand_block_markbad;
 
 
-	/* and make the autooob the default one */
-	memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
-
-	mtd->owner = THIS_MODULE;
+	/* propagate ecc.layout to mtd_info */
+	mtd->ecclayout = chip->ecc.layout;
 
 
 	/* Check, if we should skip the bad block table scan */
 	/* Check, if we should skip the bad block table scan */
-	if (this->options & NAND_SKIP_BBTSCAN)
+	if (chip->options & NAND_SKIP_BBTSCAN)
 		return 0;
 		return 0;
 
 
 	/* Build bad block table */
 	/* Build bad block table */
-	return this->scan_bbt (mtd);
+	return chip->scan_bbt(mtd);
 }
 }
 
 
 /**
 /**
  * nand_release - [NAND Interface] Free resources held by the NAND device
  * nand_release - [NAND Interface] Free resources held by the NAND device
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
 */
 */
-void nand_release (struct mtd_info *mtd)
+void nand_release(struct mtd_info *mtd)
 {
 {
-	struct nand_chip *this = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 
 
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
 	/* Deregister partitions */
 	/* Deregister partitions */
-	del_mtd_partitions (mtd);
+	del_mtd_partitions(mtd);
 #endif
 #endif
 	/* Deregister the device */
 	/* Deregister the device */
-	del_mtd_device (mtd);
+	del_mtd_device(mtd);
 
 
 	/* Free bad block table memory */
 	/* Free bad block table memory */
-	kfree (this->bbt);
-	/* Buffer allocated by nand_scan ? */
-	if (this->options & NAND_OOBBUF_ALLOC)
-		kfree (this->oob_buf);
-	/* Buffer allocated by nand_scan ? */
-	if (this->options & NAND_DATABUF_ALLOC)
-		kfree (this->data_buf);
+	kfree(chip->bbt);
 }
 }
 
 
-EXPORT_SYMBOL_GPL (nand_scan);
-EXPORT_SYMBOL_GPL (nand_release);
-
+EXPORT_SYMBOL_GPL(nand_scan);
+EXPORT_SYMBOL_GPL(nand_release);
 
 
 static int __init nand_base_init(void)
 static int __init nand_base_init(void)
 {
 {
@@ -2748,6 +2537,6 @@ static void __exit nand_base_exit(void)
 module_init(nand_base_init);
 module_init(nand_base_init);
 module_exit(nand_base_exit);
 module_exit(nand_base_exit);
 
 
-MODULE_LICENSE ("GPL");
-MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
-MODULE_DESCRIPTION ("Generic NAND flash driver code");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION("Generic NAND flash driver code");

+ 307 - 195
drivers/mtd/nand/nand_bbt.c

@@ -48,7 +48,7 @@
  *
  *
  * Following assumptions are made:
  * Following assumptions are made:
  * - bbts start at a page boundary, if autolocated on a block boundary
  * - bbts start at a page boundary, if autolocated on a block boundary
- * - the space neccecary for a bbt in FLASH does not exceed a block boundary
+ * - the space necessary for a bbt in FLASH does not exceed a block boundary
  *
  *
  */
  */
 
 
@@ -60,7 +60,7 @@
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/compatmac.h>
 #include <linux/bitops.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
-
+#include <linux/vmalloc.h>
 
 
 /**
 /**
  * check_pattern - [GENERIC] check if a pattern is in the buffer
  * check_pattern - [GENERIC] check if a pattern is in the buffer
@@ -75,7 +75,7 @@
  * pattern area contain 0xff
  * pattern area contain 0xff
  *
  *
 */
 */
-static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
 {
 {
 	int i, end = 0;
 	int i, end = 0;
 	uint8_t *p = buf;
 	uint8_t *p = buf;
@@ -116,7 +116,7 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
  * no optional empty check
  * no optional empty check
  *
  *
 */
 */
-static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)
+static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
 {
 {
 	int i;
 	int i;
 	uint8_t *p = buf;
 	uint8_t *p = buf;
@@ -142,8 +142,8 @@ static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)
  * Read the bad block table starting from page.
  * Read the bad block table starting from page.
  *
  *
  */
  */
-static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
-	int bits, int offs, int reserved_block_code)
+static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
+		    int bits, int offs, int reserved_block_code)
 {
 {
 	int res, i, j, act = 0;
 	int res, i, j, act = 0;
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
@@ -152,17 +152,17 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
 	uint8_t msk = (uint8_t) ((1 << bits) - 1);
 	uint8_t msk = (uint8_t) ((1 << bits) - 1);
 
 
 	totlen = (num * bits) >> 3;
 	totlen = (num * bits) >> 3;
-	from = ((loff_t)page) << this->page_shift;
+	from = ((loff_t) page) << this->page_shift;
 
 
 	while (totlen) {
 	while (totlen) {
-		len = min (totlen, (size_t) (1 << this->bbt_erase_shift));
-		res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob);
+		len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
+		res = mtd->read(mtd, from, len, &retlen, buf);
 		if (res < 0) {
 		if (res < 0) {
 			if (retlen != len) {
 			if (retlen != len) {
-				printk (KERN_INFO "nand_bbt: Error reading bad block table\n");
+				printk(KERN_INFO "nand_bbt: Error reading bad block table\n");
 				return res;
 				return res;
 			}
 			}
-			printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
+			printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
 		}
 		}
 
 
 		/* Analyse data */
 		/* Analyse data */
@@ -172,22 +172,23 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
 				uint8_t tmp = (dat >> j) & msk;
 				uint8_t tmp = (dat >> j) & msk;
 				if (tmp == msk)
 				if (tmp == msk)
 					continue;
 					continue;
-				if (reserved_block_code &&
-				    (tmp == reserved_block_code)) {
-					printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
-						((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+				if (reserved_block_code && (tmp == reserved_block_code)) {
+					printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
+					       ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
 					this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
 					this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
+					mtd->ecc_stats.bbtblocks++;
 					continue;
 					continue;
 				}
 				}
 				/* Leave it for now, if its matured we can move this
 				/* Leave it for now, if its matured we can move this
 				 * message to MTD_DEBUG_LEVEL0 */
 				 * message to MTD_DEBUG_LEVEL0 */
-				printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
-					((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+				printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
+				       ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
 				/* Factory marked bad or worn out ? */
 				/* Factory marked bad or worn out ? */
 				if (tmp == 0)
 				if (tmp == 0)
 					this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
 					this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
 				else
 				else
 					this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
 					this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
+				mtd->ecc_stats.badblocks++;
 			}
 			}
 		}
 		}
 		totlen -= len;
 		totlen -= len;
@@ -207,7 +208,7 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
  * Read the bad block table for all chips starting at a given page
  * Read the bad block table for all chips starting at a given page
  * We assume that the bbt bits are in consecutive order.
  * We assume that the bbt bits are in consecutive order.
 */
 */
-static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	int res = 0, i;
 	int res = 0, i;
@@ -231,6 +232,42 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ * Scan read raw data from flash
+ */
+static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+			 size_t len)
+{
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OOB_RAW;
+	ops.ooboffs = 0;
+	ops.ooblen = mtd->oobsize;
+	ops.oobbuf = buf;
+	ops.datbuf = buf;
+	ops.len = len;
+
+	return mtd->read_oob(mtd, offs, &ops);
+}
+
+/*
+ * Scan write data with oob to flash
+ */
+static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
+			  uint8_t *buf, uint8_t *oob)
+{
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = 0;
+	ops.ooblen = mtd->oobsize;
+	ops.datbuf = buf;
+	ops.oobbuf = oob;
+	ops.len = len;
+
+	return mtd->write_oob(mtd, offs, &ops);
+}
+
 /**
 /**
  * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
  * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -242,28 +279,85 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des
  * We assume that the bbt bits are in consecutive order.
  * We assume that the bbt bits are in consecutive order.
  *
  *
 */
 */
-static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td,
-	struct nand_bbt_descr *md)
+static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+			 struct nand_bbt_descr *td, struct nand_bbt_descr *md)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 
 
 	/* Read the primary version, if available */
 	/* Read the primary version, if available */
 	if (td->options & NAND_BBT_VERSION) {
 	if (td->options & NAND_BBT_VERSION) {
-		nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
-		td->version[0] = buf[mtd->oobblock + td->veroffs];
-		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]);
+		scan_read_raw(mtd, buf, td->pages[0] << this->page_shift,
+			      mtd->writesize);
+		td->version[0] = buf[mtd->writesize + td->veroffs];
+		printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
+		       td->pages[0], td->version[0]);
 	}
 	}
 
 
 	/* Read the mirror version, if available */
 	/* Read the mirror version, if available */
 	if (md && (md->options & NAND_BBT_VERSION)) {
 	if (md && (md->options & NAND_BBT_VERSION)) {
-		nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
-		md->version[0] = buf[mtd->oobblock + md->veroffs];
-		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
+		scan_read_raw(mtd, buf, md->pages[0] << this->page_shift,
+			      mtd->writesize);
+		md->version[0] = buf[mtd->writesize + md->veroffs];
+		printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
+		       md->pages[0], md->version[0]);
 	}
 	}
-
 	return 1;
 	return 1;
 }
 }
 
 
+/*
+ * Scan a given block full
+ */
+static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+			   loff_t offs, uint8_t *buf, size_t readlen,
+			   int scanlen, int len)
+{
+	int ret, j;
+
+	ret = scan_read_raw(mtd, buf, offs, readlen);
+	if (ret)
+		return ret;
+
+	for (j = 0; j < len; j++, buf += scanlen) {
+		if (check_pattern(buf, scanlen, mtd->writesize, bd))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Scan a given block partially
+ */
+static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+			   loff_t offs, uint8_t *buf, int len)
+{
+	struct mtd_oob_ops ops;
+	int j, ret;
+
+	ops.len = mtd->oobsize;
+	ops.ooblen = mtd->oobsize;
+	ops.oobbuf = buf;
+	ops.ooboffs = 0;
+	ops.datbuf = NULL;
+	ops.mode = MTD_OOB_PLACE;
+
+	for (j = 0; j < len; j++) {
+		/*
+		 * Read the full oob until read_oob is fixed to
+		 * handle single byte reads for 16 bit
+		 * buswidth
+		 */
+		ret = mtd->read_oob(mtd, offs, &ops);
+		if (ret)
+			return ret;
+
+		if (check_short_pattern(buf, bd))
+			return 1;
+
+		offs += mtd->writesize;
+	}
+	return 0;
+}
+
 /**
 /**
  * create_bbt - [GENERIC] Create a bad block table by scanning the device
  * create_bbt - [GENERIC] Create a bad block table by scanning the device
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -275,15 +369,16 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de
  * Create a bad block table by scanning the device
  * Create a bad block table by scanning the device
  * for the given good/bad block identify pattern
  * for the given good/bad block identify pattern
  */
  */
-static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+	struct nand_bbt_descr *bd, int chip)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
-	int i, j, numblocks, len, scanlen;
+	int i, numblocks, len, scanlen;
 	int startblock;
 	int startblock;
 	loff_t from;
 	loff_t from;
-	size_t readlen, ooblen;
+	size_t readlen;
 
 
-	printk (KERN_INFO "Scanning device for bad blocks\n");
+	printk(KERN_INFO "Scanning device for bad blocks\n");
 
 
 	if (bd->options & NAND_BBT_SCANALLPAGES)
 	if (bd->options & NAND_BBT_SCANALLPAGES)
 		len = 1 << (this->bbt_erase_shift - this->page_shift);
 		len = 1 << (this->bbt_erase_shift - this->page_shift);
@@ -296,25 +391,24 @@ static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 
 
 	if (!(bd->options & NAND_BBT_SCANEMPTY)) {
 	if (!(bd->options & NAND_BBT_SCANEMPTY)) {
 		/* We need only read few bytes from the OOB area */
 		/* We need only read few bytes from the OOB area */
-		scanlen = ooblen = 0;
+		scanlen = 0;
 		readlen = bd->len;
 		readlen = bd->len;
 	} else {
 	} else {
 		/* Full page content should be read */
 		/* Full page content should be read */
-		scanlen	= mtd->oobblock + mtd->oobsize;
-		readlen = len * mtd->oobblock;
-		ooblen = len * mtd->oobsize;
+		scanlen = mtd->writesize + mtd->oobsize;
+		readlen = len * mtd->writesize;
 	}
 	}
 
 
 	if (chip == -1) {
 	if (chip == -1) {
-		/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
-		 * makes shifting and masking less painful */
+		/* Note that numblocks is 2 * (real numblocks) here, see i+=2
+		 * below as it makes shifting and masking less painful */
 		numblocks = mtd->size >> (this->bbt_erase_shift - 1);
 		numblocks = mtd->size >> (this->bbt_erase_shift - 1);
 		startblock = 0;
 		startblock = 0;
 		from = 0;
 		from = 0;
 	} else {
 	} else {
 		if (chip >= this->numchips) {
 		if (chip >= this->numchips) {
-			printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
-				chip + 1, this->numchips);
+			printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+			       chip + 1, this->numchips);
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
 		numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
 		numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
@@ -326,36 +420,22 @@ static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 	for (i = startblock; i < numblocks;) {
 	for (i = startblock; i < numblocks;) {
 		int ret;
 		int ret;
 
 
-		if (bd->options & NAND_BBT_SCANEMPTY)
-			if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))
-				return ret;
-
-		for (j = 0; j < len; j++) {
-			if (!(bd->options & NAND_BBT_SCANEMPTY)) {
-				size_t retlen;
-
-				/* Read the full oob until read_oob is fixed to
-				 * handle single byte reads for 16 bit buswidth */
-				ret = mtd->read_oob(mtd, from + j * mtd->oobblock,
-							mtd->oobsize, &retlen, buf);
-				if (ret)
-					return ret;
-
-				if (check_short_pattern (buf, bd)) {
-					this->bbt[i >> 3] |= 0x03 << (i & 0x6);
-					printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
-						i >> 1, (unsigned int) from);
-					break;
-				}
-			} else {
-				if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
-					this->bbt[i >> 3] |= 0x03 << (i & 0x6);
-					printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
-						i >> 1, (unsigned int) from);
-					break;
-				}
-			}
+		if (bd->options & NAND_BBT_SCANALLPAGES)
+			ret = scan_block_full(mtd, bd, from, buf, readlen,
+					      scanlen, len);
+		else
+			ret = scan_block_fast(mtd, bd, from, buf, len);
+
+		if (ret < 0)
+			return ret;
+
+		if (ret) {
+			this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+			printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
+			       i >> 1, (unsigned int)from);
+			mtd->ecc_stats.badblocks++;
 		}
 		}
+
 		i += 2;
 		i += 2;
 		from += (1 << this->bbt_erase_shift);
 		from += (1 << this->bbt_erase_shift);
 	}
 	}
@@ -374,22 +454,23 @@ static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
  * block.
  * block.
  * If the option NAND_BBT_PERCHIP is given, each chip is searched
  * If the option NAND_BBT_PERCHIP is given, each chip is searched
  * for a bbt, which contains the bad block information of this chip.
  * for a bbt, which contains the bad block information of this chip.
- * This is neccecary to provide support for certain DOC devices.
+ * This is necessary to provide support for certain DOC devices.
  *
  *
  * The bbt ident pattern resides in the oob area of the first page
  * The bbt ident pattern resides in the oob area of the first page
  * in a block.
  * in a block.
  */
  */
-static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	int i, chips;
 	int i, chips;
 	int bits, startblock, block, dir;
 	int bits, startblock, block, dir;
-	int scanlen = mtd->oobblock + mtd->oobsize;
+	int scanlen = mtd->writesize + mtd->oobsize;
 	int bbtblocks;
 	int bbtblocks;
+	int blocktopage = this->bbt_erase_shift - this->page_shift;
 
 
 	/* Search direction top -> down ? */
 	/* Search direction top -> down ? */
 	if (td->options & NAND_BBT_LASTBLOCK) {
 	if (td->options & NAND_BBT_LASTBLOCK) {
-		startblock = (mtd->size >> this->bbt_erase_shift) -1;
+		startblock = (mtd->size >> this->bbt_erase_shift) - 1;
 		dir = -1;
 		dir = -1;
 	} else {
 	} else {
 		startblock = 0;
 		startblock = 0;
@@ -415,13 +496,16 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 		td->pages[i] = -1;
 		td->pages[i] = -1;
 		/* Scan the maximum number of blocks */
 		/* Scan the maximum number of blocks */
 		for (block = 0; block < td->maxblocks; block++) {
 		for (block = 0; block < td->maxblocks; block++) {
+
 			int actblock = startblock + dir * block;
 			int actblock = startblock + dir * block;
+			loff_t offs = actblock << this->bbt_erase_shift;
+
 			/* Read first page */
 			/* Read first page */
-			nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize);
-			if (!check_pattern(buf, scanlen, mtd->oobblock, td)) {
-				td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
+			scan_read_raw(mtd, buf, offs, mtd->writesize);
+			if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
+				td->pages[i] = actblock << blocktopage;
 				if (td->options & NAND_BBT_VERSION) {
 				if (td->options & NAND_BBT_VERSION) {
-					td->version[i] = buf[mtd->oobblock + td->veroffs];
+					td->version[i] = buf[mtd->writesize + td->veroffs];
 				}
 				}
 				break;
 				break;
 			}
 			}
@@ -431,9 +515,10 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 	/* Check, if we found a bbt for each requested chip */
 	/* Check, if we found a bbt for each requested chip */
 	for (i = 0; i < chips; i++) {
 	for (i = 0; i < chips; i++) {
 		if (td->pages[i] == -1)
 		if (td->pages[i] == -1)
-			printk (KERN_WARNING "Bad block table not found for chip %d\n", i);
+			printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
 		else
 		else
-			printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]);
+			printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
+			       td->version[i]);
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -447,21 +532,19 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
  *
  *
  * Search and read the bad block table(s)
  * Search and read the bad block table(s)
 */
 */
-static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
-	struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
 {
 {
 	/* Search the primary table */
 	/* Search the primary table */
-	search_bbt (mtd, buf, td);
+	search_bbt(mtd, buf, td);
 
 
 	/* Search the mirror table */
 	/* Search the mirror table */
 	if (md)
 	if (md)
-		search_bbt (mtd, buf, md);
+		search_bbt(mtd, buf, md);
 
 
 	/* Force result check */
 	/* Force result check */
 	return 1;
 	return 1;
 }
 }
 
 
-
 /**
 /**
  * write_bbt - [GENERIC] (Re)write the bad block table
  * write_bbt - [GENERIC] (Re)write the bad block table
  *
  *
@@ -474,25 +557,31 @@ static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
  * (Re)write the bad block table
  * (Re)write the bad block table
  *
  *
 */
 */
-static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
-	struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel)
+static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+		     struct nand_bbt_descr *td, struct nand_bbt_descr *md,
+		     int chipsel)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
-	struct nand_oobinfo oobinfo;
 	struct erase_info einfo;
 	struct erase_info einfo;
 	int i, j, res, chip = 0;
 	int i, j, res, chip = 0;
 	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
 	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
-	int nrchips, bbtoffs, pageoffs;
+	int nrchips, bbtoffs, pageoffs, ooboffs;
 	uint8_t msk[4];
 	uint8_t msk[4];
 	uint8_t rcode = td->reserved_block_code;
 	uint8_t rcode = td->reserved_block_code;
 	size_t retlen, len = 0;
 	size_t retlen, len = 0;
 	loff_t to;
 	loff_t to;
+	struct mtd_oob_ops ops;
+
+	ops.ooblen = mtd->oobsize;
+	ops.ooboffs = 0;
+	ops.datbuf = NULL;
+	ops.mode = MTD_OOB_PLACE;
 
 
 	if (!rcode)
 	if (!rcode)
 		rcode = 0xff;
 		rcode = 0xff;
 	/* Write bad block table per chip rather than per device ? */
 	/* Write bad block table per chip rather than per device ? */
 	if (td->options & NAND_BBT_PERCHIP) {
 	if (td->options & NAND_BBT_PERCHIP) {
-		numblocks = (int) (this->chipsize >> this->bbt_erase_shift);
+		numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
 		/* Full device write or specific chip ? */
 		/* Full device write or specific chip ? */
 		if (chipsel == -1) {
 		if (chipsel == -1) {
 			nrchips = this->numchips;
 			nrchips = this->numchips;
@@ -501,7 +590,7 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
 			chip = chipsel;
 			chip = chipsel;
 		}
 		}
 	} else {
 	} else {
-		numblocks = (int) (mtd->size >> this->bbt_erase_shift);
+		numblocks = (int)(mtd->size >> this->bbt_erase_shift);
 		nrchips = 1;
 		nrchips = 1;
 	}
 	}
 
 
@@ -530,27 +619,38 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
 		for (i = 0; i < td->maxblocks; i++) {
 		for (i = 0; i < td->maxblocks; i++) {
 			int block = startblock + dir * i;
 			int block = startblock + dir * i;
 			/* Check, if the block is bad */
 			/* Check, if the block is bad */
-			switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) {
+			switch ((this->bbt[block >> 2] >>
+				 (2 * (block & 0x03))) & 0x03) {
 			case 0x01:
 			case 0x01:
 			case 0x03:
 			case 0x03:
 				continue;
 				continue;
 			}
 			}
-			page = block << (this->bbt_erase_shift - this->page_shift);
+			page = block <<
+				(this->bbt_erase_shift - this->page_shift);
 			/* Check, if the block is used by the mirror table */
 			/* Check, if the block is used by the mirror table */
 			if (!md || md->pages[chip] != page)
 			if (!md || md->pages[chip] != page)
 				goto write;
 				goto write;
 		}
 		}
-		printk (KERN_ERR "No space left to write bad block table\n");
+		printk(KERN_ERR "No space left to write bad block table\n");
 		return -ENOSPC;
 		return -ENOSPC;
-write:
+	write:
 
 
 		/* Set up shift count and masks for the flash table */
 		/* Set up shift count and masks for the flash table */
 		bits = td->options & NAND_BBT_NRBITS_MSK;
 		bits = td->options & NAND_BBT_NRBITS_MSK;
+		msk[2] = ~rcode;
 		switch (bits) {
 		switch (bits) {
-		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break;
-		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break;
-		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break;
-		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break;
+		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
+			msk[3] = 0x01;
+			break;
+		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
+			msk[3] = 0x03;
+			break;
+		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
+			msk[3] = 0x0f;
+			break;
+		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
+			msk[3] = 0xff;
+			break;
 		default: return -EINVAL;
 		default: return -EINVAL;
 		}
 		}
 
 
@@ -558,82 +658,92 @@ write:
 
 
 		to = ((loff_t) page) << this->page_shift;
 		to = ((loff_t) page) << this->page_shift;
 
 
-		memcpy (&oobinfo, this->autooob, sizeof(oobinfo));
-		oobinfo.useecc = MTD_NANDECC_PLACEONLY;
-
 		/* Must we save the block contents ? */
 		/* Must we save the block contents ? */
 		if (td->options & NAND_BBT_SAVECONTENT) {
 		if (td->options & NAND_BBT_SAVECONTENT) {
 			/* Make it block aligned */
 			/* Make it block aligned */
 			to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
 			to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
 			len = 1 << this->bbt_erase_shift;
 			len = 1 << this->bbt_erase_shift;
-			res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+			res = mtd->read(mtd, to, len, &retlen, buf);
 			if (res < 0) {
 			if (res < 0) {
 				if (retlen != len) {
 				if (retlen != len) {
-					printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n");
+					printk(KERN_INFO "nand_bbt: Error "
+					       "reading block for writing "
+					       "the bad block table\n");
 					return res;
 					return res;
 				}
 				}
-				printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n");
+				printk(KERN_WARNING "nand_bbt: ECC error "
+				       "while reading block for writing "
+				       "bad block table\n");
 			}
 			}
+			/* Read oob data */
+			ops.len = (len >> this->page_shift) * mtd->oobsize;
+			ops.oobbuf = &buf[len];
+			res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
+			if (res < 0 || ops.retlen != ops.len)
+				goto outerr;
+
 			/* Calc the byte offset in the buffer */
 			/* Calc the byte offset in the buffer */
 			pageoffs = page - (int)(to >> this->page_shift);
 			pageoffs = page - (int)(to >> this->page_shift);
 			offs = pageoffs << this->page_shift;
 			offs = pageoffs << this->page_shift;
 			/* Preset the bbt area with 0xff */
 			/* Preset the bbt area with 0xff */
-			memset (&buf[offs], 0xff, (size_t)(numblocks >> sft));
-			/* Preset the bbt's oob area with 0xff */
-			memset (&buf[len + pageoffs * mtd->oobsize], 0xff,
-				((len >> this->page_shift) - pageoffs) * mtd->oobsize);
-			if (td->options & NAND_BBT_VERSION) {
-				buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip];
-			}
+			memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
+			ooboffs = len + (pageoffs * mtd->oobsize);
+
 		} else {
 		} else {
 			/* Calc length */
 			/* Calc length */
 			len = (size_t) (numblocks >> sft);
 			len = (size_t) (numblocks >> sft);
 			/* Make it page aligned ! */
 			/* Make it page aligned ! */
-			len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1);
+			len = (len + (mtd->writesize - 1)) &
+				~(mtd->writesize - 1);
 			/* Preset the buffer with 0xff */
 			/* Preset the buffer with 0xff */
-			memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize);
+			memset(buf, 0xff, len +
+			       (len >> this->page_shift)* mtd->oobsize);
 			offs = 0;
 			offs = 0;
+			ooboffs = len;
 			/* Pattern is located in oob area of first page */
 			/* Pattern is located in oob area of first page */
-			memcpy (&buf[len + td->offs], td->pattern, td->len);
-			if (td->options & NAND_BBT_VERSION) {
-				buf[len + td->veroffs] = td->version[chip];
-			}
+			memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
 		}
 		}
 
 
+		if (td->options & NAND_BBT_VERSION)
+			buf[ooboffs + td->veroffs] = td->version[chip];
+
 		/* walk through the memory table */
 		/* walk through the memory table */
-		for (i = 0; i < numblocks; ) {
+		for (i = 0; i < numblocks;) {
 			uint8_t dat;
 			uint8_t dat;
 			dat = this->bbt[bbtoffs + (i >> 2)];
 			dat = this->bbt[bbtoffs + (i >> 2)];
-			for (j = 0; j < 4; j++ , i++) {
+			for (j = 0; j < 4; j++, i++) {
 				int sftcnt = (i << (3 - sft)) & sftmsk;
 				int sftcnt = (i << (3 - sft)) & sftmsk;
 				/* Do not store the reserved bbt blocks ! */
 				/* Do not store the reserved bbt blocks ! */
-				buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt);
+				buf[offs + (i >> sft)] &=
+					~(msk[dat & 0x03] << sftcnt);
 				dat >>= 2;
 				dat >>= 2;
 			}
 			}
 		}
 		}
 
 
-		memset (&einfo, 0, sizeof (einfo));
+		memset(&einfo, 0, sizeof(einfo));
 		einfo.mtd = mtd;
 		einfo.mtd = mtd;
-		einfo.addr = (unsigned long) to;
+		einfo.addr = (unsigned long)to;
 		einfo.len = 1 << this->bbt_erase_shift;
 		einfo.len = 1 << this->bbt_erase_shift;
-		res = nand_erase_nand (mtd, &einfo, 1);
-		if (res < 0) {
-			printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res);
-			return res;
-		}
+		res = nand_erase_nand(mtd, &einfo, 1);
+		if (res < 0)
+			goto outerr;
 
 
-		res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
-		if (res < 0) {
-			printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res);
-			return res;
-		}
-		printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n",
-			(unsigned int) to, td->version[chip]);
+		res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
+		if (res < 0)
+			goto outerr;
+
+		printk(KERN_DEBUG "Bad block table written to 0x%08x, version "
+		       "0x%02X\n", (unsigned int)to, td->version[chip]);
 
 
 		/* Mark it as used */
 		/* Mark it as used */
 		td->pages[chip] = page;
 		td->pages[chip] = page;
 	}
 	}
 	return 0;
 	return 0;
+
+ outerr:
+	printk(KERN_WARNING
+	       "nand_bbt: Error while writing bad block table %d\n", res);
+	return res;
 }
 }
 
 
 /**
 /**
@@ -644,27 +754,27 @@ write:
  * The function creates a memory based bbt by scanning the device
  * The function creates a memory based bbt by scanning the device
  * for manufacturer / software marked good / bad blocks
  * for manufacturer / software marked good / bad blocks
 */
 */
-static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 
 
 	bd->options &= ~NAND_BBT_SCANEMPTY;
 	bd->options &= ~NAND_BBT_SCANEMPTY;
-	return create_bbt (mtd, this->data_buf, bd, -1);
+	return create_bbt(mtd, this->buffers.databuf, bd, -1);
 }
 }
 
 
 /**
 /**
- * check_create - [GENERIC] create and write bbt(s) if neccecary
+ * check_create - [GENERIC] create and write bbt(s) if necessary
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
  * @buf:	temporary buffer
  * @buf:	temporary buffer
  * @bd:		descriptor for the good/bad block search pattern
  * @bd:		descriptor for the good/bad block search pattern
  *
  *
  * The function checks the results of the previous call to read_bbt
  * The function checks the results of the previous call to read_bbt
- * and creates / updates the bbt(s) if neccecary
- * Creation is neccecary if no bbt was found for the chip/device
- * Update is neccecary if one of the tables is missing or the
+ * and creates / updates the bbt(s) if necessary
+ * Creation is necessary if no bbt was found for the chip/device
+ * Update is necessary if one of the tables is missing or the
  * version nr. of one table is less than the other
  * version nr. of one table is less than the other
 */
 */
-static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
 {
 {
 	int i, chips, writeops, chipsel, res;
 	int i, chips, writeops, chipsel, res;
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
@@ -732,35 +842,35 @@ static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des
 			rd = td;
 			rd = td;
 			goto writecheck;
 			goto writecheck;
 		}
 		}
-create:
+	create:
 		/* Create the bad block table by scanning the device ? */
 		/* Create the bad block table by scanning the device ? */
 		if (!(td->options & NAND_BBT_CREATE))
 		if (!(td->options & NAND_BBT_CREATE))
 			continue;
 			continue;
 
 
 		/* Create the table in memory by scanning the chip(s) */
 		/* Create the table in memory by scanning the chip(s) */
-		create_bbt (mtd, buf, bd, chipsel);
+		create_bbt(mtd, buf, bd, chipsel);
 
 
 		td->version[i] = 1;
 		td->version[i] = 1;
 		if (md)
 		if (md)
 			md->version[i] = 1;
 			md->version[i] = 1;
-writecheck:
+	writecheck:
 		/* read back first ? */
 		/* read back first ? */
 		if (rd)
 		if (rd)
-			read_abs_bbt (mtd, buf, rd, chipsel);
+			read_abs_bbt(mtd, buf, rd, chipsel);
 		/* If they weren't versioned, read both. */
 		/* If they weren't versioned, read both. */
 		if (rd2)
 		if (rd2)
-			read_abs_bbt (mtd, buf, rd2, chipsel);
+			read_abs_bbt(mtd, buf, rd2, chipsel);
 
 
 		/* Write the bad block table to the device ? */
 		/* Write the bad block table to the device ? */
 		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
 		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
-			res = write_bbt (mtd, buf, td, md, chipsel);
+			res = write_bbt(mtd, buf, td, md, chipsel);
 			if (res < 0)
 			if (res < 0)
 				return res;
 				return res;
 		}
 		}
 
 
 		/* Write the mirror bad block table to the device ? */
 		/* Write the mirror bad block table to the device ? */
 		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
 		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
-			res = write_bbt (mtd, buf, md, td, chipsel);
+			res = write_bbt(mtd, buf, md, td, chipsel);
 			if (res < 0)
 			if (res < 0)
 				return res;
 				return res;
 		}
 		}
@@ -777,7 +887,7 @@ writecheck:
  * accidental erasures / writes. The regions are identified by
  * accidental erasures / writes. The regions are identified by
  * the mark 0x02.
  * the mark 0x02.
 */
 */
-static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
+static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	int i, j, chips, block, nrblocks, update;
 	int i, j, chips, block, nrblocks, update;
@@ -795,7 +905,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
 	for (i = 0; i < chips; i++) {
 	for (i = 0; i < chips; i++) {
 		if ((td->options & NAND_BBT_ABSPAGE) ||
 		if ((td->options & NAND_BBT_ABSPAGE) ||
 		    !(td->options & NAND_BBT_WRITE)) {
 		    !(td->options & NAND_BBT_WRITE)) {
-		    	if (td->pages[i] == -1) continue;
+			if (td->pages[i] == -1)
+				continue;
 			block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
 			block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
 			block <<= 1;
 			block <<= 1;
 			oldval = this->bbt[(block >> 3)];
 			oldval = this->bbt[(block >> 3)];
@@ -815,7 +926,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
 			oldval = this->bbt[(block >> 3)];
 			oldval = this->bbt[(block >> 3)];
 			newval = oldval | (0x2 << (block & 0x06));
 			newval = oldval | (0x2 << (block & 0x06));
 			this->bbt[(block >> 3)] = newval;
 			this->bbt[(block >> 3)] = newval;
-			if (oldval != newval) update = 1;
+			if (oldval != newval)
+				update = 1;
 			block += 2;
 			block += 2;
 		}
 		}
 		/* If we want reserved blocks to be recorded to flash, and some
 		/* If we want reserved blocks to be recorded to flash, and some
@@ -840,7 +952,7 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
  * by calling the nand_free_bbt function.
  * by calling the nand_free_bbt function.
  *
  *
 */
 */
-int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	int len, res = 0;
 	int len, res = 0;
@@ -850,21 +962,21 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
 
 
 	len = mtd->size >> (this->bbt_erase_shift + 2);
 	len = mtd->size >> (this->bbt_erase_shift + 2);
 	/* Allocate memory (2bit per block) */
 	/* Allocate memory (2bit per block) */
-	this->bbt = kmalloc (len, GFP_KERNEL);
+	this->bbt = kmalloc(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 */
 	/* Clear the memory bad block table */
-	memset (this->bbt, 0x00, len);
+	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
 	 */
 	 */
 	if (!td) {
 	if (!td) {
 		if ((res = nand_memory_bbt(mtd, bd))) {
 		if ((res = nand_memory_bbt(mtd, bd))) {
-			printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
-			kfree (this->bbt);
+			printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
+			kfree(this->bbt);
 			this->bbt = NULL;
 			this->bbt = NULL;
 		}
 		}
 		return res;
 		return res;
@@ -873,35 +985,34 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
 	/* Allocate a temporary buffer for one eraseblock incl. oob */
 	/* Allocate a temporary buffer for one eraseblock incl. oob */
 	len = (1 << this->bbt_erase_shift);
 	len = (1 << this->bbt_erase_shift);
 	len += (len >> this->page_shift) * mtd->oobsize;
 	len += (len >> this->page_shift) * mtd->oobsize;
-	buf = kmalloc (len, GFP_KERNEL);
+	buf = vmalloc(len);
 	if (!buf) {
 	if (!buf) {
-		printk (KERN_ERR "nand_bbt: Out of memory\n");
-		kfree (this->bbt);
+		printk(KERN_ERR "nand_bbt: Out of memory\n");
+		kfree(this->bbt);
 		this->bbt = NULL;
 		this->bbt = NULL;
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
 	/* Is the bbt at a given page ? */
 	/* Is the bbt at a given page ? */
 	if (td->options & NAND_BBT_ABSPAGE) {
 	if (td->options & NAND_BBT_ABSPAGE) {
-		res = read_abs_bbts (mtd, buf, td, md);
+		res = read_abs_bbts(mtd, buf, td, md);
 	} else {
 	} else {
 		/* Search the bad block table using a pattern in oob */
 		/* Search the bad block table using a pattern in oob */
-		res = search_read_bbts (mtd, buf, td, md);
+		res = search_read_bbts(mtd, buf, td, md);
 	}
 	}
 
 
 	if (res)
 	if (res)
-		res = check_create (mtd, buf, bd);
+		res = check_create(mtd, buf, bd);
 
 
 	/* Prevent the bbt regions from erasing / writing */
 	/* Prevent the bbt regions from erasing / writing */
-	mark_bbt_region (mtd, td);
+	mark_bbt_region(mtd, td);
 	if (md)
 	if (md)
-		mark_bbt_region (mtd, md);
+		mark_bbt_region(mtd, md);
 
 
-	kfree (buf);
+	vfree(buf);
 	return res;
 	return res;
 }
 }
 
 
-
 /**
 /**
  * nand_update_bbt - [NAND Interface] update bad block table(s)
  * nand_update_bbt - [NAND Interface] update bad block table(s)
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -909,7 +1020,7 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
  *
  *
  * The function updates the bad block table(s)
  * The function updates the bad block table(s)
 */
 */
-int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
+int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	int len, res = 0, writeops = 0;
 	int len, res = 0, writeops = 0;
@@ -925,9 +1036,9 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
 	/* Allocate a temporary buffer for one eraseblock incl. oob */
 	/* Allocate a temporary buffer for one eraseblock incl. oob */
 	len = (1 << this->bbt_erase_shift);
 	len = (1 << this->bbt_erase_shift);
 	len += (len >> this->page_shift) * mtd->oobsize;
 	len += (len >> this->page_shift) * mtd->oobsize;
-	buf = kmalloc (len, GFP_KERNEL);
+	buf = kmalloc(len, GFP_KERNEL);
 	if (!buf) {
 	if (!buf) {
-		printk (KERN_ERR "nand_update_bbt: Out of memory\n");
+		printk(KERN_ERR "nand_update_bbt: Out of memory\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
@@ -935,7 +1046,7 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
 
 
 	/* Do we have a bbt per chip ? */
 	/* Do we have a bbt per chip ? */
 	if (td->options & NAND_BBT_PERCHIP) {
 	if (td->options & NAND_BBT_PERCHIP) {
-		chip = (int) (offs >> this->chip_shift);
+		chip = (int)(offs >> this->chip_shift);
 		chipsel = chip;
 		chipsel = chip;
 	} else {
 	} else {
 		chip = 0;
 		chip = 0;
@@ -948,17 +1059,17 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
 
 
 	/* Write the bad block table to the device ? */
 	/* Write the bad block table to the device ? */
 	if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
 	if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
-		res = write_bbt (mtd, buf, td, md, chipsel);
+		res = write_bbt(mtd, buf, td, md, chipsel);
 		if (res < 0)
 		if (res < 0)
 			goto out;
 			goto out;
 	}
 	}
 	/* Write the mirror bad block table to the device ? */
 	/* Write the mirror bad block table to the device ? */
 	if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
 	if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
-		res = write_bbt (mtd, buf, md, td, chipsel);
+		res = write_bbt(mtd, buf, md, td, chipsel);
 	}
 	}
 
 
-out:
-	kfree (buf);
+ out:
+	kfree(buf);
 	return res;
 	return res;
 }
 }
 
 
@@ -981,14 +1092,14 @@ static struct nand_bbt_descr largepage_memorybased = {
 };
 };
 
 
 static struct nand_bbt_descr smallpage_flashbased = {
 static struct nand_bbt_descr smallpage_flashbased = {
-	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.options = NAND_BBT_SCAN2NDPAGE,
 	.offs = 5,
 	.offs = 5,
 	.len = 1,
 	.len = 1,
 	.pattern = scan_ff_pattern
 	.pattern = scan_ff_pattern
 };
 };
 
 
 static struct nand_bbt_descr largepage_flashbased = {
 static struct nand_bbt_descr largepage_flashbased = {
-	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.options = NAND_BBT_SCAN2NDPAGE,
 	.offs = 0,
 	.offs = 0,
 	.len = 2,
 	.len = 2,
 	.pattern = scan_ff_pattern
 	.pattern = scan_ff_pattern
@@ -1036,7 +1147,7 @@ static struct nand_bbt_descr bbt_mirror_descr = {
  * support for the device and calls the nand_scan_bbt function
  * support for the device and calls the nand_scan_bbt function
  *
  *
 */
 */
-int nand_default_bbt (struct mtd_info *mtd)
+int nand_default_bbt(struct mtd_info *mtd)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 
 
@@ -1046,7 +1157,7 @@ int nand_default_bbt (struct mtd_info *mtd)
 	 * of the good / bad information, so we _must_ store
 	 * of the good / bad information, so we _must_ store
 	 * this information in a good / bad table during
 	 * this information in a good / bad table during
 	 * startup
 	 * startup
-	*/
+	 */
 	if (this->options & NAND_IS_AND) {
 	if (this->options & NAND_IS_AND) {
 		/* Use the default pattern descriptors */
 		/* Use the default pattern descriptors */
 		if (!this->bbt_td) {
 		if (!this->bbt_td) {
@@ -1054,10 +1165,9 @@ int nand_default_bbt (struct mtd_info *mtd)
 			this->bbt_md = &bbt_mirror_descr;
 			this->bbt_md = &bbt_mirror_descr;
 		}
 		}
 		this->options |= NAND_USE_FLASH_BBT;
 		this->options |= NAND_USE_FLASH_BBT;
-		return nand_scan_bbt (mtd, &agand_flashbased);
+		return nand_scan_bbt(mtd, &agand_flashbased);
 	}
 	}
 
 
-
 	/* Is a flash based bad block table requested ? */
 	/* Is a flash based bad block table requested ? */
 	if (this->options & NAND_USE_FLASH_BBT) {
 	if (this->options & NAND_USE_FLASH_BBT) {
 		/* Use the default pattern descriptors */
 		/* Use the default pattern descriptors */
@@ -1066,18 +1176,17 @@ int nand_default_bbt (struct mtd_info *mtd)
 			this->bbt_md = &bbt_mirror_descr;
 			this->bbt_md = &bbt_mirror_descr;
 		}
 		}
 		if (!this->badblock_pattern) {
 		if (!this->badblock_pattern) {
-			this->badblock_pattern = (mtd->oobblock > 512) ?
-				&largepage_flashbased : &smallpage_flashbased;
+			this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
 		}
 		}
 	} else {
 	} else {
 		this->bbt_td = NULL;
 		this->bbt_td = NULL;
 		this->bbt_md = NULL;
 		this->bbt_md = NULL;
 		if (!this->badblock_pattern) {
 		if (!this->badblock_pattern) {
-			this->badblock_pattern = (mtd->oobblock > 512) ?
-				&largepage_memorybased : &smallpage_memorybased;
+			this->badblock_pattern = (mtd->writesize > 512) ?
+			    &largepage_memorybased : &smallpage_memorybased;
 		}
 		}
 	}
 	}
-	return nand_scan_bbt (mtd, this->badblock_pattern);
+	return nand_scan_bbt(mtd, this->badblock_pattern);
 }
 }
 
 
 /**
 /**
@@ -1087,26 +1196,29 @@ int nand_default_bbt (struct mtd_info *mtd)
  * @allowbbt:	allow access to bad block table region
  * @allowbbt:	allow access to bad block table region
  *
  *
 */
 */
-int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
+int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	int block;
 	int block;
-	uint8_t	res;
+	uint8_t res;
 
 
 	/* Get block number * 2 */
 	/* Get block number * 2 */
-	block = (int) (offs >> (this->bbt_erase_shift - 1));
+	block = (int)(offs >> (this->bbt_erase_shift - 1));
 	res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
 	res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
 
 
-	DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
-		(unsigned int)offs, block >> 1, res);
+	DEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+	      (unsigned int)offs, block >> 1, res);
 
 
 	switch ((int)res) {
 	switch ((int)res) {
-	case 0x00:	return 0;
-	case 0x01:	return 1;
-	case 0x02:	return allowbbt ? 0 : 1;
+	case 0x00:
+		return 0;
+	case 0x01:
+		return 1;
+	case 0x02:
+		return allowbbt ? 0 : 1;
 	}
 	}
 	return 1;
 	return 1;
 }
 }
 
 
-EXPORT_SYMBOL (nand_scan_bbt);
-EXPORT_SYMBOL (nand_default_bbt);
+EXPORT_SYMBOL(nand_scan_bbt);
+EXPORT_SYMBOL(nand_default_bbt);

+ 88 - 139
drivers/mtd/nand/nand_ecc.c

@@ -7,6 +7,8 @@
  * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
  * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
  *                         Toshiba America Electronics Components, Inc.
  *                         Toshiba America Electronics Components, Inc.
  *
  *
+ * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
+ *
  * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $
  * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $
  *
  *
  * This file is free software; you can redistribute it and/or modify it
  * This file is free software; you can redistribute it and/or modify it
@@ -62,90 +64,76 @@ static const u_char nand_ecc_precalc_table[] = {
 	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
 	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
 };
 };
 
 
-
 /**
 /**
- * nand_trans_result - [GENERIC] create non-inverted ECC
- * @reg2:	line parity reg 2
- * @reg3:	line parity reg 3
- * @ecc_code:	ecc
- *
- * Creates non-inverted ECC code from line parity
- */
-static void nand_trans_result(u_char reg2, u_char reg3,
-	u_char *ecc_code)
-{
-	u_char a, b, i, tmp1, tmp2;
-
-	/* Initialize variables */
-	a = b = 0x80;
-	tmp1 = tmp2 = 0;
-
-	/* Calculate first ECC byte */
-	for (i = 0; i < 4; i++) {
-		if (reg3 & a)		/* LP15,13,11,9 --> ecc_code[0] */
-			tmp1 |= b;
-		b >>= 1;
-		if (reg2 & a)		/* LP14,12,10,8 --> ecc_code[0] */
-			tmp1 |= b;
-		b >>= 1;
-		a >>= 1;
-	}
-
-	/* Calculate second ECC byte */
-	b = 0x80;
-	for (i = 0; i < 4; i++) {
-		if (reg3 & a)		/* LP7,5,3,1 --> ecc_code[1] */
-			tmp2 |= b;
-		b >>= 1;
-		if (reg2 & a)		/* LP6,4,2,0 --> ecc_code[1] */
-			tmp2 |= b;
-		b >>= 1;
-		a >>= 1;
-	}
-
-	/* Store two of the ECC bytes */
-	ecc_code[0] = tmp1;
-	ecc_code[1] = tmp2;
-}
-
-/**
- * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block
+ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code
+ *			for 256 byte block
  * @mtd:	MTD block structure
  * @mtd:	MTD block structure
  * @dat:	raw data
  * @dat:	raw data
  * @ecc_code:	buffer for ECC
  * @ecc_code:	buffer for ECC
  */
  */
-int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+		       u_char *ecc_code)
 {
 {
-	u_char idx, reg1, reg2, reg3;
-	int j;
+	uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
+	int i;
 
 
 	/* Initialize variables */
 	/* Initialize variables */
 	reg1 = reg2 = reg3 = 0;
 	reg1 = reg2 = reg3 = 0;
-	ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
 
 
 	/* Build up column parity */
 	/* Build up column parity */
-	for(j = 0; j < 256; j++) {
-
+	for(i = 0; i < 256; i++) {
 		/* Get CP0 - CP5 from table */
 		/* Get CP0 - CP5 from table */
-		idx = nand_ecc_precalc_table[dat[j]];
+		idx = nand_ecc_precalc_table[*dat++];
 		reg1 ^= (idx & 0x3f);
 		reg1 ^= (idx & 0x3f);
 
 
 		/* All bit XOR = 1 ? */
 		/* All bit XOR = 1 ? */
 		if (idx & 0x40) {
 		if (idx & 0x40) {
-			reg3 ^= (u_char) j;
-			reg2 ^= ~((u_char) j);
+			reg3 ^= (uint8_t) i;
+			reg2 ^= ~((uint8_t) i);
 		}
 		}
 	}
 	}
 
 
 	/* Create non-inverted ECC code from line parity */
 	/* Create non-inverted ECC code from line parity */
-	nand_trans_result(reg2, reg3, ecc_code);
+	tmp1  = (reg3 & 0x80) >> 0; /* B7 -> B7 */
+	tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
+	tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
+	tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
+	tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
+	tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
+	tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
+	tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
+
+	tmp2  = (reg3 & 0x08) << 4; /* B3 -> B7 */
+	tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
+	tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
+	tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
+	tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
+	tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
+	tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
+	tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
 
 
 	/* Calculate final ECC code */
 	/* Calculate final ECC code */
-	ecc_code[0] = ~ecc_code[0];
-	ecc_code[1] = ~ecc_code[1];
+#ifdef CONFIG_NAND_ECC_SMC
+	ecc_code[0] = ~tmp2;
+	ecc_code[1] = ~tmp1;
+#else
+	ecc_code[0] = ~tmp1;
+	ecc_code[1] = ~tmp2;
+#endif
 	ecc_code[2] = ((~reg1) << 2) | 0x03;
 	ecc_code[2] = ((~reg1) << 2) | 0x03;
+
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL(nand_calculate_ecc);
+
+static inline int countbits(uint32_t byte)
+{
+	int res = 0;
+
+	for (;byte; byte >>= 1)
+		res += byte & 0x01;
+	return res;
+}
 
 
 /**
 /**
  * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
  * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
@@ -156,93 +144,54 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code
  *
  *
  * Detect and correct a 1 bit error for 256 byte block
  * Detect and correct a 1 bit error for 256 byte block
  */
  */
-int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+int nand_correct_data(struct mtd_info *mtd, u_char *dat,
+		      u_char *read_ecc, u_char *calc_ecc)
 {
 {
-	u_char a, b, c, d1, d2, d3, add, bit, i;
+	uint8_t s0, s1, s2;
+
+#ifdef CONFIG_NAND_ECC_SMC
+	s0 = calc_ecc[0] ^ read_ecc[0];
+	s1 = calc_ecc[1] ^ read_ecc[1];
+	s2 = calc_ecc[2] ^ read_ecc[2];
+#else
+	s1 = calc_ecc[0] ^ read_ecc[0];
+	s0 = calc_ecc[1] ^ read_ecc[1];
+	s2 = calc_ecc[2] ^ read_ecc[2];
+#endif
+	if ((s0 | s1 | s2) == 0)
+		return 0;
 
 
-	/* Do error detection */
-	d1 = calc_ecc[0] ^ read_ecc[0];
-	d2 = calc_ecc[1] ^ read_ecc[1];
-	d3 = calc_ecc[2] ^ read_ecc[2];
+	/* Check for a single bit error */
+	if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
+	    ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
+	    ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
 
 
-	if ((d1 | d2 | d3) == 0) {
-		/* No errors */
-		return 0;
-	}
-	else {
-		a = (d1 ^ (d1 >> 1)) & 0x55;
-		b = (d2 ^ (d2 >> 1)) & 0x55;
-		c = (d3 ^ (d3 >> 1)) & 0x54;
-
-		/* Found and will correct single bit error in the data */
-		if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
-			c = 0x80;
-			add = 0;
-			a = 0x80;
-			for (i=0; i<4; i++) {
-				if (d1 & c)
-					add |= a;
-				c >>= 2;
-				a >>= 1;
-			}
-			c = 0x80;
-			for (i=0; i<4; i++) {
-				if (d2 & c)
-					add |= a;
-				c >>= 2;
-				a >>= 1;
-			}
-			bit = 0;
-			b = 0x04;
-			c = 0x80;
-			for (i=0; i<3; i++) {
-				if (d3 & c)
-					bit |= b;
-				c >>= 2;
-				b >>= 1;
-			}
-			b = 0x01;
-			a = dat[add];
-			a ^= (b << bit);
-			dat[add] = a;
-			return 1;
-		}
-		else {
-			i = 0;
-			while (d1) {
-				if (d1 & 0x01)
-					++i;
-				d1 >>= 1;
-			}
-			while (d2) {
-				if (d2 & 0x01)
-					++i;
-				d2 >>= 1;
-			}
-			while (d3) {
-				if (d3 & 0x01)
-					++i;
-				d3 >>= 1;
-			}
-			if (i == 1) {
-				/* ECC Code Error Correction */
-				read_ecc[0] = calc_ecc[0];
-				read_ecc[1] = calc_ecc[1];
-				read_ecc[2] = calc_ecc[2];
-				return 2;
-			}
-			else {
-				/* Uncorrectable Error */
-				return -1;
-			}
-		}
+		uint32_t byteoffs, bitnum;
+
+		byteoffs = (s1 << 0) & 0x80;
+		byteoffs |= (s1 << 1) & 0x40;
+		byteoffs |= (s1 << 2) & 0x20;
+		byteoffs |= (s1 << 3) & 0x10;
+
+		byteoffs |= (s0 >> 4) & 0x08;
+		byteoffs |= (s0 >> 3) & 0x04;
+		byteoffs |= (s0 >> 2) & 0x02;
+		byteoffs |= (s0 >> 1) & 0x01;
+
+		bitnum = (s2 >> 5) & 0x04;
+		bitnum |= (s2 >> 4) & 0x02;
+		bitnum |= (s2 >> 3) & 0x01;
+
+		dat[byteoffs] ^= (1 << bitnum);
+
+		return 1;
 	}
 	}
 
 
-	/* Should never happen */
+	if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
+		return 1;
+
 	return -1;
 	return -1;
 }
 }
-
-EXPORT_SYMBOL(nand_calculate_ecc);
 EXPORT_SYMBOL(nand_correct_data);
 EXPORT_SYMBOL(nand_correct_data);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 94 - 83
drivers/mtd/nand/nand_ids.c

@@ -18,99 +18,110 @@
 *	Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
 *	Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
 *	options
 *	options
 *
 *
-* 	Pagesize; 0, 256, 512
-*	0 	get this information from the extended chip ID
+*	Pagesize; 0, 256, 512
+*	0	get this information from the extended chip ID
 +	256	256 Byte page size
 +	256	256 Byte page size
 *	512	512 Byte page size
 *	512	512 Byte page size
 */
 */
 struct nand_flash_dev nand_flash_ids[] = {
 struct nand_flash_dev nand_flash_ids[] = {
-	{"NAND 1MiB 5V 8-bit", 		0x6e, 256, 1, 0x1000, 0},
-	{"NAND 2MiB 5V 8-bit", 		0x64, 256, 2, 0x1000, 0},
-	{"NAND 4MiB 5V 8-bit", 		0x6b, 512, 4, 0x2000, 0},
-	{"NAND 1MiB 3,3V 8-bit", 	0xe8, 256, 1, 0x1000, 0},
-	{"NAND 1MiB 3,3V 8-bit", 	0xec, 256, 1, 0x1000, 0},
-	{"NAND 2MiB 3,3V 8-bit", 	0xea, 256, 2, 0x1000, 0},
-	{"NAND 4MiB 3,3V 8-bit", 	0xd5, 512, 4, 0x2000, 0},
-	{"NAND 4MiB 3,3V 8-bit", 	0xe3, 512, 4, 0x2000, 0},
-	{"NAND 4MiB 3,3V 8-bit", 	0xe5, 512, 4, 0x2000, 0},
-	{"NAND 8MiB 3,3V 8-bit", 	0xd6, 512, 8, 0x2000, 0},
-
-	{"NAND 8MiB 1,8V 8-bit", 	0x39, 512, 8, 0x2000, 0},
-	{"NAND 8MiB 3,3V 8-bit", 	0xe6, 512, 8, 0x2000, 0},
-	{"NAND 8MiB 1,8V 16-bit", 	0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
-	{"NAND 8MiB 3,3V 16-bit", 	0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
-
-	{"NAND 16MiB 1,8V 8-bit", 	0x33, 512, 16, 0x4000, 0},
-	{"NAND 16MiB 3,3V 8-bit", 	0x73, 512, 16, 0x4000, 0},
-	{"NAND 16MiB 1,8V 16-bit", 	0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
-	{"NAND 16MiB 3,3V 16-bit", 	0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
-
-	{"NAND 32MiB 1,8V 8-bit", 	0x35, 512, 32, 0x4000, 0},
-	{"NAND 32MiB 3,3V 8-bit", 	0x75, 512, 32, 0x4000, 0},
-	{"NAND 32MiB 1,8V 16-bit", 	0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
-	{"NAND 32MiB 3,3V 16-bit", 	0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
-
-	{"NAND 64MiB 1,8V 8-bit", 	0x36, 512, 64, 0x4000, 0},
-	{"NAND 64MiB 3,3V 8-bit", 	0x76, 512, 64, 0x4000, 0},
-	{"NAND 64MiB 1,8V 16-bit", 	0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
-	{"NAND 64MiB 3,3V 16-bit", 	0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
-
-	{"NAND 128MiB 1,8V 8-bit", 	0x78, 512, 128, 0x4000, 0},
-	{"NAND 128MiB 1,8V 8-bit", 	0x39, 512, 128, 0x4000, 0},
-	{"NAND 128MiB 3,3V 8-bit", 	0x79, 512, 128, 0x4000, 0},
-	{"NAND 128MiB 1,8V 16-bit", 	0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
-	{"NAND 128MiB 1,8V 16-bit", 	0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
-	{"NAND 128MiB 3,3V 16-bit", 	0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
-	{"NAND 128MiB 3,3V 16-bit", 	0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
-
-	{"NAND 256MiB 3,3V 8-bit", 	0x71, 512, 256, 0x4000, 0},
-
-	/* These are the new chips with large page size. The pagesize
-	* and the erasesize is determined from the extended id bytes
-	*/
+	{"NAND 1MiB 5V 8-bit",		0x6e, 256, 1, 0x1000, 0},
+	{"NAND 2MiB 5V 8-bit",		0x64, 256, 2, 0x1000, 0},
+	{"NAND 4MiB 5V 8-bit",		0x6b, 512, 4, 0x2000, 0},
+	{"NAND 1MiB 3,3V 8-bit",	0xe8, 256, 1, 0x1000, 0},
+	{"NAND 1MiB 3,3V 8-bit",	0xec, 256, 1, 0x1000, 0},
+	{"NAND 2MiB 3,3V 8-bit",	0xea, 256, 2, 0x1000, 0},
+	{"NAND 4MiB 3,3V 8-bit",	0xd5, 512, 4, 0x2000, 0},
+	{"NAND 4MiB 3,3V 8-bit",	0xe3, 512, 4, 0x2000, 0},
+	{"NAND 4MiB 3,3V 8-bit",	0xe5, 512, 4, 0x2000, 0},
+	{"NAND 8MiB 3,3V 8-bit",	0xd6, 512, 8, 0x2000, 0},
+
+	{"NAND 8MiB 1,8V 8-bit",	0x39, 512, 8, 0x2000, 0},
+	{"NAND 8MiB 3,3V 8-bit",	0xe6, 512, 8, 0x2000, 0},
+	{"NAND 8MiB 1,8V 16-bit",	0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+	{"NAND 8MiB 3,3V 16-bit",	0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+
+	{"NAND 16MiB 1,8V 8-bit",	0x33, 512, 16, 0x4000, 0},
+	{"NAND 16MiB 3,3V 8-bit",	0x73, 512, 16, 0x4000, 0},
+	{"NAND 16MiB 1,8V 16-bit",	0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 16MiB 3,3V 16-bit",	0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
+
+	{"NAND 32MiB 1,8V 8-bit",	0x35, 512, 32, 0x4000, 0},
+	{"NAND 32MiB 3,3V 8-bit",	0x75, 512, 32, 0x4000, 0},
+	{"NAND 32MiB 1,8V 16-bit",	0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 32MiB 3,3V 16-bit",	0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
+
+	{"NAND 64MiB 1,8V 8-bit",	0x36, 512, 64, 0x4000, 0},
+	{"NAND 64MiB 3,3V 8-bit",	0x76, 512, 64, 0x4000, 0},
+	{"NAND 64MiB 1,8V 16-bit",	0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 64MiB 3,3V 16-bit",	0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
+
+	{"NAND 128MiB 1,8V 8-bit",	0x78, 512, 128, 0x4000, 0},
+	{"NAND 128MiB 1,8V 8-bit",	0x39, 512, 128, 0x4000, 0},
+	{"NAND 128MiB 3,3V 8-bit",	0x79, 512, 128, 0x4000, 0},
+	{"NAND 128MiB 1,8V 16-bit",	0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 128MiB 1,8V 16-bit",	0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 128MiB 3,3V 16-bit",	0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 128MiB 3,3V 16-bit",	0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+
+	{"NAND 256MiB 3,3V 8-bit",	0x71, 512, 256, 0x4000, 0},
+
+	/*
+	 * These are the new chips with large page size. The pagesize and the
+	 * erasesize is determined from the extended id bytes
+	 */
+#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR)
+#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
+
 	/*512 Megabit */
 	/*512 Megabit */
-	{"NAND 64MiB 1,8V 8-bit", 	0xA2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 64MiB 3,3V 8-bit", 	0xF2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 64MiB 1,8V 16-bit", 	0xB2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-	{"NAND 64MiB 3,3V 16-bit", 	0xC2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 64MiB 1,8V 8-bit",	0xA2, 0,  64, 0, LP_OPTIONS},
+	{"NAND 64MiB 3,3V 8-bit",	0xF2, 0,  64, 0, LP_OPTIONS},
+	{"NAND 64MiB 1,8V 16-bit",	0xB2, 0,  64, 0, LP_OPTIONS16},
+	{"NAND 64MiB 3,3V 16-bit",	0xC2, 0,  64, 0, LP_OPTIONS16},
 
 
 	/* 1 Gigabit */
 	/* 1 Gigabit */
-	{"NAND 128MiB 1,8V 8-bit", 	0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 128MiB 3,3V 8-bit", 	0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 128MiB 1,8V 16-bit", 	0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-	{"NAND 128MiB 3,3V 16-bit", 	0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 128MiB 1,8V 8-bit",	0xA1, 0, 128, 0, LP_OPTIONS},
+	{"NAND 128MiB 3,3V 8-bit",	0xF1, 0, 128, 0, LP_OPTIONS},
+	{"NAND 128MiB 1,8V 16-bit",	0xB1, 0, 128, 0, LP_OPTIONS16},
+	{"NAND 128MiB 3,3V 16-bit",	0xC1, 0, 128, 0, LP_OPTIONS16},
 
 
 	/* 2 Gigabit */
 	/* 2 Gigabit */
-	{"NAND 256MiB 1,8V 8-bit", 	0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 256MiB 3,3V 8-bit", 	0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 256MiB 1,8V 16-bit", 	0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-	{"NAND 256MiB 3,3V 16-bit", 	0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 256MiB 1,8V 8-bit",	0xAA, 0, 256, 0, LP_OPTIONS},
+	{"NAND 256MiB 3,3V 8-bit",	0xDA, 0, 256, 0, LP_OPTIONS},
+	{"NAND 256MiB 1,8V 16-bit",	0xBA, 0, 256, 0, LP_OPTIONS16},
+	{"NAND 256MiB 3,3V 16-bit",	0xCA, 0, 256, 0, LP_OPTIONS16},
 
 
 	/* 4 Gigabit */
 	/* 4 Gigabit */
-	{"NAND 512MiB 1,8V 8-bit", 	0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 512MiB 3,3V 8-bit", 	0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 512MiB 1,8V 16-bit", 	0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-	{"NAND 512MiB 3,3V 16-bit", 	0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 512MiB 1,8V 8-bit",	0xAC, 0, 512, 0, LP_OPTIONS},
+	{"NAND 512MiB 3,3V 8-bit",	0xDC, 0, 512, 0, LP_OPTIONS},
+	{"NAND 512MiB 1,8V 16-bit",	0xBC, 0, 512, 0, LP_OPTIONS16},
+	{"NAND 512MiB 3,3V 16-bit",	0xCC, 0, 512, 0, LP_OPTIONS16},
 
 
 	/* 8 Gigabit */
 	/* 8 Gigabit */
-	{"NAND 1GiB 1,8V 8-bit", 	0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 1GiB 3,3V 8-bit", 	0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 1GiB 1,8V 16-bit", 	0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-	{"NAND 1GiB 3,3V 16-bit", 	0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 1GiB 1,8V 8-bit",	0xA3, 0, 1024, 0, LP_OPTIONS},
+	{"NAND 1GiB 3,3V 8-bit",	0xD3, 0, 1024, 0, LP_OPTIONS},
+	{"NAND 1GiB 1,8V 16-bit",	0xB3, 0, 1024, 0, LP_OPTIONS16},
+	{"NAND 1GiB 3,3V 16-bit",	0xC3, 0, 1024, 0, LP_OPTIONS16},
 
 
 	/* 16 Gigabit */
 	/* 16 Gigabit */
-	{"NAND 2GiB 1,8V 8-bit", 	0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 2GiB 3,3V 8-bit", 	0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
-	{"NAND 2GiB 1,8V 16-bit", 	0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-	{"NAND 2GiB 3,3V 16-bit", 	0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-
-	/* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout !
-	 * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes
-	 * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7
-	 * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go
-	 * There are more speed improvements for reads and writes possible, but not implemented now
+	{"NAND 2GiB 1,8V 8-bit",	0xA5, 0, 2048, 0, LP_OPTIONS},
+	{"NAND 2GiB 3,3V 8-bit",	0xD5, 0, 2048, 0, LP_OPTIONS},
+	{"NAND 2GiB 1,8V 16-bit",	0xB5, 0, 2048, 0, LP_OPTIONS16},
+	{"NAND 2GiB 3,3V 16-bit",	0xC5, 0, 2048, 0, LP_OPTIONS16},
+
+	/*
+	 * Renesas AND 1 Gigabit. Those chips do not support extended id and
+	 * have a strange page/block layout !  The chosen minimum erasesize is
+	 * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page
+	 * planes 1 block = 2 pages, but due to plane arrangement the blocks
+	 * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would
+	 * increase the eraseblock size so we chose a combined one which can be
+	 * erased in one go There are more speed improvements for reads and
+	 * writes possible, but not implemented now
 	 */
 	 */
-	{"AND 128MiB 3,3V 8-bit",	0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
+	{"AND 128MiB 3,3V 8-bit",	0x01, 2048, 128, 0x4000,
+	 NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY |
+	 BBT_AUTO_REFRESH
+	},
 
 
 	{NULL,}
 	{NULL,}
 };
 };
@@ -125,13 +136,13 @@ struct nand_manufacturers nand_manuf_ids[] = {
 	{NAND_MFR_NATIONAL, "National"},
 	{NAND_MFR_NATIONAL, "National"},
 	{NAND_MFR_RENESAS, "Renesas"},
 	{NAND_MFR_RENESAS, "Renesas"},
 	{NAND_MFR_STMICRO, "ST Micro"},
 	{NAND_MFR_STMICRO, "ST Micro"},
-        {NAND_MFR_HYNIX, "Hynix"},
+	{NAND_MFR_HYNIX, "Hynix"},
 	{0x0, "Unknown"}
 	{0x0, "Unknown"}
 };
 };
 
 
-EXPORT_SYMBOL (nand_manuf_ids);
-EXPORT_SYMBOL (nand_flash_ids);
+EXPORT_SYMBOL(nand_manuf_ids);
+EXPORT_SYMBOL(nand_flash_ids);
 
 
-MODULE_LICENSE ("GPL");
-MODULE_AUTHOR ("Thomas Gleixner <tglx@linutronix.de>");
-MODULE_DESCRIPTION ("Nand device & manufacturer ID's");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION("Nand device & manufacturer IDs");

+ 17 - 78
drivers/mtd/nand/nandsim.c

@@ -369,7 +369,7 @@ init_nandsim(struct mtd_info *mtd)
 	/* Initialize the NAND flash parameters */
 	/* Initialize the NAND flash parameters */
 	ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
 	ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
 	ns->geom.totsz    = mtd->size;
 	ns->geom.totsz    = mtd->size;
-	ns->geom.pgsz     = mtd->oobblock;
+	ns->geom.pgsz     = mtd->writesize;
 	ns->geom.oobsz    = mtd->oobsize;
 	ns->geom.oobsz    = mtd->oobsize;
 	ns->geom.secsz    = mtd->erasesize;
 	ns->geom.secsz    = mtd->erasesize;
 	ns->geom.pgszoob  = ns->geom.pgsz + ns->geom.oobsz;
 	ns->geom.pgszoob  = ns->geom.pgsz + ns->geom.oobsz;
@@ -1071,68 +1071,6 @@ switch_state(struct nandsim *ns)
 	}
 	}
 }
 }
 
 
-static void
-ns_hwcontrol(struct mtd_info *mtd, int cmd)
-{
-	struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
-
-	switch (cmd) {
-
-	/* set CLE line high */
-	case NAND_CTL_SETCLE:
-		NS_DBG("ns_hwcontrol: start command latch cycles\n");
-		ns->lines.cle  = 1;
-		break;
-
-	/* set CLE line low */
-	case NAND_CTL_CLRCLE:
-		NS_DBG("ns_hwcontrol: stop command latch cycles\n");
-		ns->lines.cle  = 0;
-		break;
-
-	/* set ALE line high */
-	case NAND_CTL_SETALE:
-		NS_DBG("ns_hwcontrol: start address latch cycles\n");
-		ns->lines.ale   = 1;
-		break;
-
-	/* set ALE line low */
-	case NAND_CTL_CLRALE:
-		NS_DBG("ns_hwcontrol: stop address latch cycles\n");
-		ns->lines.ale  = 0;
-		break;
-
-	/* set WP line high */
-	case NAND_CTL_SETWP:
-		NS_DBG("ns_hwcontrol: enable write protection\n");
-		ns->lines.wp = 1;
-		break;
-
-	/* set WP line low */
-	case NAND_CTL_CLRWP:
-		NS_DBG("ns_hwcontrol: disable write protection\n");
-		ns->lines.wp = 0;
-		break;
-
-	/* set CE line low */
-	case NAND_CTL_SETNCE:
-		NS_DBG("ns_hwcontrol: enable chip\n");
-		ns->lines.ce = 1;
-		break;
-
-	/* set CE line high */
-	case NAND_CTL_CLRNCE:
-		NS_DBG("ns_hwcontrol: disable chip\n");
-		ns->lines.ce = 0;
-		break;
-
-	default:
-		NS_ERR("hwcontrol: unknown command\n");
-        }
-
-	return;
-}
-
 static u_char
 static u_char
 ns_nand_read_byte(struct mtd_info *mtd)
 ns_nand_read_byte(struct mtd_info *mtd)
 {
 {
@@ -1359,6 +1297,18 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
 	return;
 	return;
 }
 }
 
 
+static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
+{
+	struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+
+	ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
+	ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
+	ns->lines.ce = bitmask & NAND_NCE ? 1 : 0;
+
+	if (cmd != NAND_CMD_NONE)
+		ns_nand_write_byte(mtd, cmd);
+}
+
 static int
 static int
 ns_device_ready(struct mtd_info *mtd)
 ns_device_ready(struct mtd_info *mtd)
 {
 {
@@ -1376,17 +1326,6 @@ 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_word(struct mtd_info *mtd, uint16_t word)
-{
-	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
-
-	NS_DBG("write_word\n");
-
-	chip->write_byte(mtd, word & 0xFF);
-	chip->write_byte(mtd, word >> 8);
-}
-
 static void
 static void
 ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
@@ -1514,16 +1453,14 @@ static int __init ns_init_module(void)
 	/*
 	/*
 	 * Register simulator's callbacks.
 	 * Register simulator's callbacks.
 	 */
 	 */
-	chip->hwcontrol  = ns_hwcontrol;
+	chip->cmd_ctrl	 = ns_hwcontrol;
 	chip->read_byte  = ns_nand_read_byte;
 	chip->read_byte  = ns_nand_read_byte;
 	chip->dev_ready  = ns_device_ready;
 	chip->dev_ready  = ns_device_ready;
-	chip->write_byte = ns_nand_write_byte;
 	chip->write_buf  = ns_nand_write_buf;
 	chip->write_buf  = ns_nand_write_buf;
 	chip->read_buf   = ns_nand_read_buf;
 	chip->read_buf   = ns_nand_read_buf;
 	chip->verify_buf = ns_nand_verify_buf;
 	chip->verify_buf = ns_nand_verify_buf;
-	chip->write_word = ns_nand_write_word;
 	chip->read_word  = ns_nand_read_word;
 	chip->read_word  = ns_nand_read_word;
-	chip->eccmode    = NAND_ECC_SOFT;
+	chip->ecc.mode   = NAND_ECC_SOFT;
 	chip->options   |= NAND_SKIP_BBTSCAN;
 	chip->options   |= NAND_SKIP_BBTSCAN;
 
 
 	/*
 	/*
@@ -1546,6 +1483,8 @@ static int __init ns_init_module(void)
 		chip->options |= NAND_BUSWIDTH_16;
 		chip->options |= NAND_BUSWIDTH_16;
 	}
 	}
 
 
+	nsmtd->owner = THIS_MODULE;
+
 	if ((retval = nand_scan(nsmtd, 1)) != 0) {
 	if ((retval = nand_scan(nsmtd, 1)) != 0) {
 		NS_ERR("can't register NAND Simulator\n");
 		NS_ERR("can't register NAND Simulator\n");
 		if (retval > 0)
 		if (retval > 0)

+ 311 - 0
drivers/mtd/nand/ndfc.c

@@ -0,0 +1,311 @@
+/*
+ *  drivers/mtd/ndfc.c
+ *
+ *  Overview:
+ *   Platform independend driver for NDFC (NanD Flash Controller)
+ *   integrated into EP440 cores
+ *
+ *  Author: Thomas Gleixner
+ *
+ *  Copyright 2006 IBM
+ *
+ *  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/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/ndfc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/ibm44x.h>
+
+struct ndfc_nand_mtd {
+	struct mtd_info			mtd;
+	struct nand_chip		chip;
+	struct platform_nand_chip	*pl_chip;
+};
+
+static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS];
+
+struct ndfc_controller {
+	void __iomem		*ndfcbase;
+	struct nand_hw_control	ndfc_control;
+	atomic_t		childs_active;
+};
+
+static struct ndfc_controller ndfc_ctrl;
+
+static void ndfc_select_chip(struct mtd_info *mtd, int chip)
+{
+	uint32_t ccr;
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+	struct nand_chip *nandchip = mtd->priv;
+	struct ndfc_nand_mtd *nandmtd = nandchip->priv;
+	struct platform_nand_chip *pchip = nandmtd->pl_chip;
+
+	ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
+	if (chip >= 0) {
+		ccr &= ~NDFC_CCR_BS_MASK;
+		ccr |= NDFC_CCR_BS(chip + pchip->chip_offset);
+	} else
+		ccr |= NDFC_CCR_RESET_CE;
+	writel(ccr, ndfc->ndfcbase + NDFC_CCR);
+}
+
+static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	if (ctrl & NAND_CLE)
+		writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_CMD);
+	else
+		writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_ALE);
+}
+
+static int ndfc_ready(struct mtd_info *mtd)
+{
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+
+	return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
+}
+
+static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	uint32_t ccr;
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+
+	ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
+	ccr |= NDFC_CCR_RESET_ECC;
+	__raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
+	wmb();
+}
+
+static int ndfc_calculate_ecc(struct mtd_info *mtd,
+			      const u_char *dat, u_char *ecc_code)
+{
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+	uint32_t ecc;
+	uint8_t *p = (uint8_t *)&ecc;
+
+	wmb();
+	ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC);
+	ecc_code[0] = p[1];
+	ecc_code[1] = p[2];
+	ecc_code[2] = p[3];
+
+	return 0;
+}
+
+/*
+ * Speedups for buffer read/write/verify
+ *
+ * NDFC allows 32bit read/write of data. So we can speed up the buffer
+ * functions. No further checking, as nand_base will always read/write
+ * page aligned.
+ */
+static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+	uint32_t *p = (uint32_t *) buf;
+
+	for(;len > 0; len -= 4)
+		*p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA);
+}
+
+static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+	uint32_t *p = (uint32_t *) buf;
+
+	for(;len > 0; len -= 4)
+		__raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA);
+}
+
+static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+	uint32_t *p = (uint32_t *) buf;
+
+	for(;len > 0; len -= 4)
+		if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA))
+			return -EFAULT;
+	return 0;
+}
+
+/*
+ * Initialize chip structure
+ */
+static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
+{
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+	struct nand_chip *chip = &mtd->chip;
+
+	chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
+	chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
+	chip->cmd_ctrl = ndfc_hwcontrol;
+	chip->dev_ready = ndfc_ready;
+	chip->select_chip = ndfc_select_chip;
+	chip->chip_delay = 50;
+	chip->priv = mtd;
+	chip->options = mtd->pl_chip->options;
+	chip->controller = &ndfc->ndfc_control;
+	chip->read_buf = ndfc_read_buf;
+	chip->write_buf = ndfc_write_buf;
+	chip->verify_buf = ndfc_verify_buf;
+	chip->ecc.correct = nand_correct_data;
+	chip->ecc.hwctl = ndfc_enable_hwecc;
+	chip->ecc.calculate = ndfc_calculate_ecc;
+	chip->ecc.mode = NAND_ECC_HW;
+	chip->ecc.size = 256;
+	chip->ecc.bytes = 3;
+	chip->ecclayout = mtd->pl_chip->ecclayout;
+	mtd->mtd.priv = chip;
+	mtd->mtd.owner = THIS_MODULE;
+}
+
+static int ndfc_chip_probe(struct platform_device *pdev)
+{
+	struct platform_nand_chip *nc = pdev->dev.platform_data;
+	struct ndfc_chip_settings *settings = nc->priv;
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+	struct ndfc_nand_mtd *nandmtd;
+
+	if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS)
+		return -EINVAL;
+
+	/* Set the bank settings */
+	__raw_writel(settings->bank_settings,
+		     ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2));
+
+	nandmtd = &ndfc_mtd[pdev->id];
+	if (nandmtd->pl_chip)
+		return -EBUSY;
+
+	nandmtd->pl_chip = nc;
+	ndfc_chip_init(nandmtd);
+
+	/* Scan for chips */
+	if (nand_scan(&nandmtd->mtd, nc->nr_chips)) {
+		nandmtd->pl_chip = NULL;
+		return -ENODEV;
+	}
+
+#ifdef CONFIG_MTD_PARTITIONS
+	printk("Number of partitions %d\n", nc->nr_partitions);
+	if (nc->nr_partitions) {
+		/* Add the full device, so complete dumps can be made */
+		add_mtd_device(&nandmtd->mtd);
+		add_mtd_partitions(&nandmtd->mtd, nc->partitions,
+				   nc->nr_partitions);
+
+	} else
+#else
+		add_mtd_device(&nandmtd->mtd);
+#endif
+
+	atomic_inc(&ndfc->childs_active);
+	return 0;
+}
+
+static int ndfc_chip_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int ndfc_nand_probe(struct platform_device *pdev)
+{
+	struct platform_nand_ctrl *nc = pdev->dev.platform_data;
+	struct ndfc_controller_settings *settings = nc->priv;
+	struct resource *res = pdev->resource;
+	struct ndfc_controller *ndfc = &ndfc_ctrl;
+	unsigned long long phys = settings->ndfc_erpn | res->start;
+
+	ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
+	if (!ndfc->ndfcbase) {
+		printk(KERN_ERR "NDFC: ioremap failed\n");
+		return -EIO;
+	}
+
+	__raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR);
+
+	spin_lock_init(&ndfc->ndfc_control.lock);
+	init_waitqueue_head(&ndfc->ndfc_control.wq);
+
+	platform_set_drvdata(pdev, ndfc);
+
+	printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n",
+	       __raw_readl(ndfc->ndfcbase + NDFC_REVID));
+
+	return 0;
+}
+
+static int ndfc_nand_remove(struct platform_device *pdev)
+{
+	struct ndfc_controller *ndfc = platform_get_drvdata(pdev);
+
+	if (atomic_read(&ndfc->childs_active))
+		return -EBUSY;
+
+	if (ndfc) {
+		platform_set_drvdata(pdev, NULL);
+		iounmap(ndfc_ctrl.ndfcbase);
+		ndfc_ctrl.ndfcbase = NULL;
+	}
+	return 0;
+}
+
+/* driver device registration */
+
+static struct platform_driver ndfc_chip_driver = {
+	.probe		= ndfc_chip_probe,
+	.remove		= ndfc_chip_remove,
+	.driver		= {
+		.name	= "ndfc-chip",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static struct platform_driver ndfc_nand_driver = {
+	.probe		= ndfc_nand_probe,
+	.remove		= ndfc_nand_remove,
+	.driver		= {
+		.name	= "ndfc-nand",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ndfc_nand_init(void)
+{
+	int ret;
+
+	spin_lock_init(&ndfc_ctrl.ndfc_control.lock);
+	init_waitqueue_head(&ndfc_ctrl.ndfc_control.wq);
+
+	ret = platform_driver_register(&ndfc_nand_driver);
+	if (!ret)
+		ret = platform_driver_register(&ndfc_chip_driver);
+	return ret;
+}
+
+static void __exit ndfc_nand_exit(void)
+{
+	platform_driver_unregister(&ndfc_chip_driver);
+	platform_driver_unregister(&ndfc_nand_driver);
+}
+
+module_init(ndfc_nand_init);
+module_exit(ndfc_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION("Platform driver for NDFC");

+ 134 - 122
drivers/mtd/nand/ppchameleonevb.c

@@ -58,21 +58,21 @@
 /*
 /*
  * MTD structure for PPChameleonEVB board
  * MTD structure for PPChameleonEVB board
  */
  */
-static struct mtd_info *ppchameleon_mtd 	= NULL;
+static struct mtd_info *ppchameleon_mtd = NULL;
 static struct mtd_info *ppchameleonevb_mtd = NULL;
 static struct mtd_info *ppchameleonevb_mtd = NULL;
 
 
 /*
 /*
  * Module stuff
  * Module stuff
  */
  */
-static unsigned long ppchameleon_fio_pbase 	= CFG_NAND0_PADDR;
+static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR;
 static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR;
 static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR;
 
 
 #ifdef MODULE
 #ifdef MODULE
 module_param(ppchameleon_fio_pbase, ulong, 0);
 module_param(ppchameleon_fio_pbase, ulong, 0);
 module_param(ppchameleonevb_fio_pbase, ulong, 0);
 module_param(ppchameleonevb_fio_pbase, ulong, 0);
 #else
 #else
-__setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase);
-__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
+__setup("ppchameleon_fio_pbase=", ppchameleon_fio_pbase);
+__setup("ppchameleonevb_fio_pbase=", ppchameleonevb_fio_pbase);
 #endif
 #endif
 
 
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
@@ -80,82 +80,96 @@ __setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
  * Define static partitions for flash devices
  * Define static partitions for flash devices
  */
  */
 static struct mtd_partition partition_info_hi[] = {
 static struct mtd_partition partition_info_hi[] = {
-	{ name: "PPChameleon HI Nand Flash",
-		  offset: 0,
-		  size: 128*1024*1024 }
+      { .name = "PPChameleon HI Nand Flash",
+	offset = 0,
+	.size = 128 * 1024 * 1024
+      }
 };
 };
 
 
 static struct mtd_partition partition_info_me[] = {
 static struct mtd_partition partition_info_me[] = {
-	{ name: "PPChameleon ME Nand Flash",
-		  offset: 0,
-		  size: 32*1024*1024 }
+      { .name = "PPChameleon ME Nand Flash",
+	.offset = 0,
+	.size = 32 * 1024 * 1024
+      }
 };
 };
 
 
 static struct mtd_partition partition_info_evb[] = {
 static struct mtd_partition partition_info_evb[] = {
-	{ name: "PPChameleonEVB Nand Flash",
-		  offset: 0,
-		  size: 32*1024*1024 }
+      { .name = "PPChameleonEVB Nand Flash",
+	.offset = 0,
+	.size = 32 * 1024 * 1024
+      }
 };
 };
 
 
 #define NUM_PARTITIONS 1
 #define NUM_PARTITIONS 1
 
 
-extern int parse_cmdline_partitions(struct mtd_info *master,
-				    struct mtd_partition **pparts,
-				    const char *mtd_id);
+extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, const char *mtd_id);
 #endif
 #endif
 
 
-
 /*
 /*
  *	hardware specific access to control-lines
  *	hardware specific access to control-lines
  */
  */
-static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd,
+				  unsigned int ctrl)
 {
 {
-	switch(cmd) {
-
-	case NAND_CTL_SETCLE:
-        	MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_CLRCLE:
-        	MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_SETALE:
-        	MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_CLRALE:
-        	MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_SETNCE:
+	struct nand_chip *chip = mtd->priv;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+#error Missing headerfiles. No way to fix this. -tglx
+		switch (cmd) {
+		case NAND_CTL_SETCLE:
+			MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_CLRCLE:
+			MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_SETALE:
+			MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_CLRALE:
+			MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_SETNCE:
 			MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
 			MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_CLRNCE:
+			break;
+		case NAND_CTL_CLRNCE:
 			MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
 			MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
-		break;
+			break;
+		}
 	}
 	}
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 }
 
 
-static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd,
+				     unsigned int ctrl)
 {
 {
-	switch(cmd) {
-
-	case NAND_CTL_SETCLE:
-        	MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_CLRCLE:
-        	MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_SETALE:
-        	MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_CLRALE:
-        	MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_SETNCE:
-        	MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_CLRNCE:
-        	MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
-		break;
+	struct nand_chip *chip = mtd->priv;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+#error Missing headerfiles. No way to fix this. -tglx
+		switch (cmd) {
+		case NAND_CTL_SETCLE:
+			MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_CLRCLE:
+			MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_SETALE:
+			MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_CLRALE:
+			MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_SETNCE:
+			MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_CLRNCE:
+			MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		}
 	}
 	}
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 }
 
 
 #ifdef USE_READY_BUSY_PIN
 #ifdef USE_READY_BUSY_PIN
@@ -164,15 +178,15 @@ static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
  */
  */
 static int ppchameleon_device_ready(struct mtd_info *minfo)
 static int ppchameleon_device_ready(struct mtd_info *minfo)
 {
 {
-	if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_RB_GPIO_PIN)
+	if (in_be32((volatile unsigned *)GPIO0_IR) & NAND_RB_GPIO_PIN)
 		return 1;
 		return 1;
 	return 0;
 	return 0;
 }
 }
 
 
 static int ppchameleonevb_device_ready(struct mtd_info *minfo)
 static int ppchameleonevb_device_ready(struct mtd_info *minfo)
 {
 {
-	if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
-	return 1;
+	if (in_be32((volatile unsigned *)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
+		return 1;
 	return 0;
 	return 0;
 }
 }
 #endif
 #endif
@@ -185,7 +199,7 @@ const char *part_probes_evb[] = { "cmdlinepart", NULL };
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-static int __init ppchameleonevb_init (void)
+static int __init ppchameleonevb_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
 	const char *part_type = 0;
 	const char *part_type = 0;
@@ -194,13 +208,11 @@ static int __init ppchameleonevb_init (void)
 	void __iomem *ppchameleon_fio_base;
 	void __iomem *ppchameleon_fio_base;
 	void __iomem *ppchameleonevb_fio_base;
 	void __iomem *ppchameleonevb_fio_base;
 
 
-
 	/*********************************
 	/*********************************
 	* Processor module NAND (if any) *
 	* Processor module NAND (if any) *
 	*********************************/
 	*********************************/
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) +
-						      sizeof(struct nand_chip), GFP_KERNEL);
+	ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!ppchameleon_mtd) {
 	if (!ppchameleon_mtd) {
 		printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
 		printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
 		return -ENOMEM;
 		return -ENOMEM;
@@ -208,63 +220,65 @@ static int __init ppchameleonevb_init (void)
 
 
 	/* map physical address */
 	/* map physical address */
 	ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M);
 	ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M);
-	if(!ppchameleon_fio_base) {
+	if (!ppchameleon_fio_base) {
 		printk("ioremap PPChameleon NAND flash failed\n");
 		printk("ioremap PPChameleon NAND flash failed\n");
 		kfree(ppchameleon_mtd);
 		kfree(ppchameleon_mtd);
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&ppchameleon_mtd[1]);
+	this = (struct nand_chip *)(&ppchameleon_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) ppchameleon_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(ppchameleon_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	ppchameleon_mtd->priv = this;
 	ppchameleon_mtd->priv = this;
+	ppchameleon_mtd->owner = THIS_MODULE;
 
 
-        /* Initialize GPIOs */
+	/* Initialize GPIOs */
 	/* Pin mapping for NAND chip */
 	/* Pin mapping for NAND chip */
 	/*
 	/*
-		CE	GPIO_01
-		CLE	GPIO_02
-		ALE	GPIO_03
-		R/B	GPIO_04
-	*/
+	   CE   GPIO_01
+	   CLE  GPIO_02
+	   ALE  GPIO_03
+	   R/B  GPIO_04
+	 */
 	/* output select */
 	/* output select */
-	out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xC0FFFFFF);
+	out_be32((volatile unsigned *)GPIO0_OSRH, in_be32((volatile unsigned *)GPIO0_OSRH) & 0xC0FFFFFF);
 	/* three-state select */
 	/* three-state select */
-	out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xC0FFFFFF);
+	out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xC0FFFFFF);
 	/* enable output driver */
 	/* enable output driver */
-	out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
+	out_be32((volatile unsigned *)GPIO0_TCR,
+		 in_be32((volatile unsigned *)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
 #ifdef USE_READY_BUSY_PIN
 #ifdef USE_READY_BUSY_PIN
 	/* three-state select */
 	/* three-state select */
-	out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFF3FFFFF);
+	out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xFF3FFFFF);
 	/* high-impedecence */
 	/* high-impedecence */
-	out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
+	out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
 	/* input select */
 	/* input select */
-	out_be32((volatile unsigned*)GPIO0_ISR1H, (in_be32((volatile unsigned*)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
+	out_be32((volatile unsigned *)GPIO0_ISR1H,
+		 (in_be32((volatile unsigned *)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
 #endif
 #endif
 
 
 	/* insert callbacks */
 	/* insert callbacks */
 	this->IO_ADDR_R = ppchameleon_fio_base;
 	this->IO_ADDR_R = ppchameleon_fio_base;
 	this->IO_ADDR_W = ppchameleon_fio_base;
 	this->IO_ADDR_W = ppchameleon_fio_base;
-	this->hwcontrol = ppchameleon_hwcontrol;
+	this->cmd_ctrl = ppchameleon_hwcontrol;
 #ifdef USE_READY_BUSY_PIN
 #ifdef USE_READY_BUSY_PIN
 	this->dev_ready = ppchameleon_device_ready;
 	this->dev_ready = ppchameleon_device_ready;
 #endif
 #endif
 	this->chip_delay = NAND_BIG_DELAY_US;
 	this->chip_delay = NAND_BIG_DELAY_US;
 	/* ECC mode */
 	/* ECC mode */
-	this->eccmode = NAND_ECC_SOFT;
+	this->ecc.mode = NAND_ECC_SOFT;
 
 
 	/* Scan to find existence of the device (it could not be mounted) */
 	/* Scan to find existence of the device (it could not be mounted) */
-	if (nand_scan (ppchameleon_mtd, 1)) {
+	if (nand_scan(ppchameleon_mtd, 1)) {
 		iounmap((void *)ppchameleon_fio_base);
 		iounmap((void *)ppchameleon_fio_base);
-		kfree (ppchameleon_mtd);
+		kfree(ppchameleon_mtd);
 		goto nand_evb_init;
 		goto nand_evb_init;
 	}
 	}
-
 #ifndef USE_READY_BUSY_PIN
 #ifndef USE_READY_BUSY_PIN
 	/* Adjust delay if necessary */
 	/* Adjust delay if necessary */
 	if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
 	if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
@@ -275,12 +289,11 @@ static int __init ppchameleonevb_init (void)
 	ppchameleon_mtd->name = "ppchameleon-nand";
 	ppchameleon_mtd->name = "ppchameleon-nand";
 	mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0);
 	mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0);
 	if (mtd_parts_nb > 0)
 	if (mtd_parts_nb > 0)
-	  part_type = "command line";
+		part_type = "command line";
 	else
 	else
-	  mtd_parts_nb = 0;
+		mtd_parts_nb = 0;
 #endif
 #endif
-	if (mtd_parts_nb == 0)
-	{
+	if (mtd_parts_nb == 0) {
 		if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
 		if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
 			mtd_parts = partition_info_me;
 			mtd_parts = partition_info_me;
 		else
 		else
@@ -293,13 +306,12 @@ static int __init ppchameleonevb_init (void)
 	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
 	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
 	add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb);
 	add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb);
 
 
-nand_evb_init:
+ nand_evb_init:
 	/****************************
 	/****************************
 	* EVB NAND (always present) *
 	* EVB NAND (always present) *
 	****************************/
 	****************************/
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) +
-							 sizeof(struct nand_chip), GFP_KERNEL);
+	ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!ppchameleonevb_mtd) {
 	if (!ppchameleonevb_mtd) {
 		printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
 		printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
 		return -ENOMEM;
 		return -ENOMEM;
@@ -307,77 +319,76 @@ nand_evb_init:
 
 
 	/* map physical address */
 	/* map physical address */
 	ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M);
 	ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M);
-	if(!ppchameleonevb_fio_base) {
+	if (!ppchameleonevb_fio_base) {
 		printk("ioremap PPChameleonEVB NAND flash failed\n");
 		printk("ioremap PPChameleonEVB NAND flash failed\n");
 		kfree(ppchameleonevb_mtd);
 		kfree(ppchameleonevb_mtd);
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&ppchameleonevb_mtd[1]);
+	this = (struct nand_chip *)(&ppchameleonevb_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	ppchameleonevb_mtd->priv = this;
 	ppchameleonevb_mtd->priv = this;
 
 
-        /* Initialize GPIOs */
+	/* Initialize GPIOs */
 	/* Pin mapping for NAND chip */
 	/* Pin mapping for NAND chip */
 	/*
 	/*
-		CE	GPIO_14
-		CLE	GPIO_15
-		ALE	GPIO_16
-		R/B	GPIO_31
-	*/
+	   CE   GPIO_14
+	   CLE  GPIO_15
+	   ALE  GPIO_16
+	   R/B  GPIO_31
+	 */
 	/* output select */
 	/* output select */
-	out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xFFFFFFF0);
-	out_be32((volatile unsigned*)GPIO0_OSRL, in_be32((volatile unsigned*)GPIO0_OSRL) & 0x3FFFFFFF);
+	out_be32((volatile unsigned *)GPIO0_OSRH, in_be32((volatile unsigned *)GPIO0_OSRH) & 0xFFFFFFF0);
+	out_be32((volatile unsigned *)GPIO0_OSRL, in_be32((volatile unsigned *)GPIO0_OSRL) & 0x3FFFFFFF);
 	/* three-state select */
 	/* three-state select */
-	out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0);
-	out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF);
+	out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xFFFFFFF0);
+	out_be32((volatile unsigned *)GPIO0_TSRL, in_be32((volatile unsigned *)GPIO0_TSRL) & 0x3FFFFFFF);
 	/* enable output driver */
 	/* enable output driver */
-	out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN |
+	out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN |
 		 NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
 		 NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
 #ifdef USE_READY_BUSY_PIN
 #ifdef USE_READY_BUSY_PIN
 	/* three-state select */
 	/* three-state select */
-	out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC);
+	out_be32((volatile unsigned *)GPIO0_TSRL, in_be32((volatile unsigned *)GPIO0_TSRL) & 0xFFFFFFFC);
 	/* high-impedecence */
 	/* high-impedecence */
-	out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
+	out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
 	/* input select */
 	/* input select */
-	out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
+	out_be32((volatile unsigned *)GPIO0_ISR1L,
+		 (in_be32((volatile unsigned *)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
 #endif
 #endif
 
 
 	/* insert callbacks */
 	/* insert callbacks */
 	this->IO_ADDR_R = ppchameleonevb_fio_base;
 	this->IO_ADDR_R = ppchameleonevb_fio_base;
 	this->IO_ADDR_W = ppchameleonevb_fio_base;
 	this->IO_ADDR_W = ppchameleonevb_fio_base;
-	this->hwcontrol = ppchameleonevb_hwcontrol;
+	this->cmd_ctrl = ppchameleonevb_hwcontrol;
 #ifdef USE_READY_BUSY_PIN
 #ifdef USE_READY_BUSY_PIN
 	this->dev_ready = ppchameleonevb_device_ready;
 	this->dev_ready = ppchameleonevb_device_ready;
 #endif
 #endif
 	this->chip_delay = NAND_SMALL_DELAY_US;
 	this->chip_delay = NAND_SMALL_DELAY_US;
 
 
 	/* ECC mode */
 	/* ECC mode */
-	this->eccmode = NAND_ECC_SOFT;
+	this->ecc.mode = NAND_ECC_SOFT;
 
 
 	/* Scan to find existence of the device */
 	/* Scan to find existence of the device */
-	if (nand_scan (ppchameleonevb_mtd, 1)) {
+	if (nand_scan(ppchameleonevb_mtd, 1)) {
 		iounmap((void *)ppchameleonevb_fio_base);
 		iounmap((void *)ppchameleonevb_fio_base);
-		kfree (ppchameleonevb_mtd);
+		kfree(ppchameleonevb_mtd);
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
-
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
 	ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
 	ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
 	mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0);
 	mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0);
 	if (mtd_parts_nb > 0)
 	if (mtd_parts_nb > 0)
-	  part_type = "command line";
+		part_type = "command line";
 	else
 	else
-	  mtd_parts_nb = 0;
+		mtd_parts_nb = 0;
 #endif
 #endif
-	if (mtd_parts_nb == 0)
-	{
+	if (mtd_parts_nb == 0) {
 		mtd_parts = partition_info_evb;
 		mtd_parts = partition_info_evb;
 		mtd_parts_nb = NUM_PARTITIONS;
 		mtd_parts_nb = NUM_PARTITIONS;
 		part_type = "static";
 		part_type = "static";
@@ -390,18 +401,19 @@ nand_evb_init:
 	/* Return happy */
 	/* Return happy */
 	return 0;
 	return 0;
 }
 }
+
 module_init(ppchameleonevb_init);
 module_init(ppchameleonevb_init);
 
 
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-static void __exit ppchameleonevb_cleanup (void)
+static void __exit ppchameleonevb_cleanup(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
 
 
 	/* Release resources, unregister device(s) */
 	/* Release resources, unregister device(s) */
-	nand_release (ppchameleon_mtd);
-	nand_release (ppchameleonevb_mtd);
+	nand_release(ppchameleon_mtd);
+	nand_release(ppchameleonevb_mtd);
 
 
 	/* Release iomaps */
 	/* Release iomaps */
 	this = (struct nand_chip *) &ppchameleon_mtd[1];
 	this = (struct nand_chip *) &ppchameleon_mtd[1];

+ 163 - 188
drivers/mtd/nand/rtc_from4.c

@@ -97,12 +97,12 @@ static struct mtd_info *rtc_from4_mtd = NULL;
 static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE);
 static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE);
 
 
 static const struct mtd_partition partition_info[] = {
 static const struct mtd_partition partition_info[] = {
-        {
-                .name   = "Renesas flash partition 1",
-                .offset = 0,
-                .size   = MTDPART_SIZ_FULL
-        },
+	{
+	 .name = "Renesas flash partition 1",
+	 .offset = 0,
+	 .size = MTDPART_SIZ_FULL},
 };
 };
+
 #define NUM_PARTITIONS 1
 #define NUM_PARTITIONS 1
 
 
 /*
 /*
@@ -111,8 +111,8 @@ static const struct mtd_partition partition_info[] = {
  *		NAND_BBT_CREATE and/or NAND_BBT_WRITE
  *		NAND_BBT_CREATE and/or NAND_BBT_WRITE
  *
  *
  */
  */
-static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
-static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
 
 
 static struct nand_bbt_descr rtc_from4_bbt_main_descr = {
 static struct nand_bbt_descr rtc_from4_bbt_main_descr = {
 	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
 	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
@@ -134,8 +134,6 @@ static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = {
 	.pattern = mirror_pattern
 	.pattern = mirror_pattern
 };
 };
 
 
-
-
 #ifdef RTC_FROM4_HWECC
 #ifdef RTC_FROM4_HWECC
 
 
 /* the Reed Solomon control structure */
 /* the Reed Solomon control structure */
@@ -144,15 +142,14 @@ static struct rs_control *rs_decoder;
 /*
 /*
  *      hardware specific Out Of Band information
  *      hardware specific Out Of Band information
  */
  */
-static struct nand_oobinfo rtc_from4_nand_oobinfo = {
-	.useecc = MTD_NANDECC_AUTOPLACE,
+static struct nand_ecclayout rtc_from4_nand_oobinfo = {
 	.eccbytes = 32,
 	.eccbytes = 32,
 	.eccpos = {
 	.eccpos = {
-		 0,  1,  2,  3,  4,  5,  6,  7,
-		 8,  9, 10, 11, 12, 13, 14, 15,
-		16, 17, 18, 19, 20, 21, 22, 23,
-		24, 25, 26, 27, 28, 29, 30, 31},
-	.oobfree = { {32, 32} }
+		   0, 1, 2, 3, 4, 5, 6, 7,
+		   8, 9, 10, 11, 12, 13, 14, 15,
+		   16, 17, 18, 19, 20, 21, 22, 23,
+		   24, 25, 26, 27, 28, 29, 30, 31},
+	.oobfree = {{32, 32}}
 };
 };
 
 
 /* Aargh. I missed the reversed bit order, when I
 /* Aargh. I missed the reversed bit order, when I
@@ -162,44 +159,42 @@ static struct nand_oobinfo rtc_from4_nand_oobinfo = {
  * of the ecc byte which we get from the FPGA
  * of the ecc byte which we get from the FPGA
  */
  */
 static uint8_t revbits[256] = {
 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,
+	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
 
 
-
-
 /*
 /*
  * rtc_from4_hwcontrol - hardware specific access to control-lines
  * rtc_from4_hwcontrol - hardware specific access to control-lines
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -212,35 +207,20 @@ static uint8_t revbits[256] = {
  * Address lines (A24-A22), so no action is required here.
  * Address lines (A24-A22), so no action is required here.
  *
  *
  */
  */
-static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
+static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd,
+				unsigned int ctrl)
 {
 {
-	struct nand_chip* this = (struct nand_chip *) (mtd->priv);
-
-	switch(cmd) {
+	struct nand_chip *chip = (mtd->priv);
 
 
-	case NAND_CTL_SETCLE:
-		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE);
-		break;
-	case NAND_CTL_CLRCLE:
-		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE);
-		break;
-
-	case NAND_CTL_SETALE:
-		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE);
-		break;
-	case NAND_CTL_CLRALE:
-		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE);
-		break;
+	if (cmd == NAND_CMD_NONE)
+		return;
 
 
-	case NAND_CTL_SETNCE:
-		break;
-	case NAND_CTL_CLRNCE:
-		break;
-
-	}
+	if (ctrl & NAND_CLE)
+		writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_CLE);
+	else
+		writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_ALE);
 }
 }
 
 
-
 /*
 /*
  * rtc_from4_nand_select_chip - hardware specific chip select
  * rtc_from4_nand_select_chip - hardware specific chip select
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -252,26 +232,25 @@ static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
  */
  */
 static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
 static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
 {
 {
-        struct nand_chip *this = mtd->priv;
+	struct nand_chip *this = mtd->priv;
 
 
 	this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK);
 	this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK);
 	this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK);
 	this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK);
 
 
-        switch(chip) {
+	switch (chip) {
 
 
-        case 0:		/* select slot 3 chip */
+	case 0:		/* select slot 3 chip */
 		this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3);
 		this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3);
 		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3);
 		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3);
-                break;
-        case 1:		/* select slot 4 chip */
+		break;
+	case 1:		/* select slot 4 chip */
 		this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4);
 		this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4);
 		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4);
 		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4);
-                break;
+		break;
 
 
-        }
+	}
 }
 }
 
 
-
 /*
 /*
  * rtc_from4_nand_device_ready - hardware specific ready/busy check
  * rtc_from4_nand_device_ready - hardware specific ready/busy check
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -290,7 +269,6 @@ static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
 
 
 }
 }
 
 
-
 /*
 /*
  * deplete - code to perform device recovery in case there was a power loss
  * deplete - code to perform device recovery in case there was a power loss
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -306,24 +284,23 @@ static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
  */
  */
 static void deplete(struct mtd_info *mtd, int chip)
 static void deplete(struct mtd_info *mtd, int chip)
 {
 {
-        struct nand_chip *this = mtd->priv;
+	struct nand_chip *this = mtd->priv;
 
 
-        /* wait until device is ready */
-        while (!this->dev_ready(mtd));
+	/* wait until device is ready */
+	while (!this->dev_ready(mtd)) ;
 
 
 	this->select_chip(mtd, chip);
 	this->select_chip(mtd, chip);
 
 
 	/* Send the commands for device recovery, phase 1 */
 	/* Send the commands for device recovery, phase 1 */
-	this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000);
-	this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1);
+	this->cmdfunc(mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000);
+	this->cmdfunc(mtd, NAND_CMD_DEPLETE2, -1, -1);
 
 
 	/* Send the commands for device recovery, phase 2 */
 	/* Send the commands for device recovery, phase 2 */
-	this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004);
-	this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1);
+	this->cmdfunc(mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004);
+	this->cmdfunc(mtd, NAND_CMD_DEPLETE2, -1, -1);
 
 
 }
 }
 
 
-
 #ifdef RTC_FROM4_HWECC
 #ifdef RTC_FROM4_HWECC
 /*
 /*
  * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
  * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
@@ -335,39 +312,35 @@ static void deplete(struct mtd_info *mtd, int chip)
  */
  */
 static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
 static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
 {
 {
-	volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
+	volatile unsigned short *rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
 	unsigned short status;
 	unsigned short status;
 
 
 	switch (mode) {
 	switch (mode) {
-	    case NAND_ECC_READ :
-		status =  RTC_FROM4_RS_ECC_CTL_CLR
-			| RTC_FROM4_RS_ECC_CTL_FD_E;
+	case NAND_ECC_READ:
+		status = RTC_FROM4_RS_ECC_CTL_CLR | RTC_FROM4_RS_ECC_CTL_FD_E;
 
 
 		*rs_ecc_ctl = status;
 		*rs_ecc_ctl = status;
 		break;
 		break;
 
 
-	    case NAND_ECC_READSYN :
-		status =  0x00;
+	case NAND_ECC_READSYN:
+		status = 0x00;
 
 
 		*rs_ecc_ctl = status;
 		*rs_ecc_ctl = status;
 		break;
 		break;
 
 
-	    case NAND_ECC_WRITE :
-		status =  RTC_FROM4_RS_ECC_CTL_CLR
-			| RTC_FROM4_RS_ECC_CTL_GEN
-			| RTC_FROM4_RS_ECC_CTL_FD_E;
+	case NAND_ECC_WRITE:
+		status = RTC_FROM4_RS_ECC_CTL_CLR | RTC_FROM4_RS_ECC_CTL_GEN | RTC_FROM4_RS_ECC_CTL_FD_E;
 
 
 		*rs_ecc_ctl = status;
 		*rs_ecc_ctl = status;
 		break;
 		break;
 
 
-	    default:
+	default:
 		BUG();
 		BUG();
 		break;
 		break;
 	}
 	}
 
 
 }
 }
 
 
-
 /*
 /*
  * rtc_from4_calculate_ecc - hardware specific code to read ECC code
  * rtc_from4_calculate_ecc - hardware specific code to read ECC code
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -383,7 +356,7 @@ static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
  */
  */
 static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
 static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
 {
 {
-	volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
+	volatile unsigned short *rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
 	unsigned short value;
 	unsigned short value;
 	int i;
 	int i;
 
 
@@ -395,7 +368,6 @@ static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_c
 	ecc_code[7] |= 0x0f;	/* set the last four bits (not used) */
 	ecc_code[7] |= 0x0f;	/* set the last four bits (not used) */
 }
 }
 
 
-
 /*
 /*
  * rtc_from4_correct_data - hardware specific code to correct data using ECC code
  * rtc_from4_correct_data - hardware specific code to correct data using ECC code
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -414,7 +386,7 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
 	unsigned short status;
 	unsigned short status;
 	uint16_t par[6], syn[6];
 	uint16_t par[6], syn[6];
 	uint8_t ecc[8];
 	uint8_t ecc[8];
-        volatile unsigned short *rs_ecc;
+	volatile unsigned short *rs_ecc;
 
 
 	status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK));
 	status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK));
 
 
@@ -424,23 +396,18 @@ 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++) {
-                ecc[i] = revbits[(*rs_ecc) & 0xFF];
-                rs_ecc++;
-        }
+	for (i = 0; i < 8; i++) {
+		ecc[i] = revbits[(*rs_ecc) & 0xFF];
+		rs_ecc++;
+	}
 
 
 	/* convert into 6 10bit syndrome fields */
 	/* convert into 6 10bit syndrome fields */
-	par[5] = rs_decoder->index_of[(((uint16_t)ecc[0] >> 0) & 0x0ff) |
-				      (((uint16_t)ecc[1] << 8) & 0x300)];
-	par[4] = rs_decoder->index_of[(((uint16_t)ecc[1] >> 2) & 0x03f) |
-				      (((uint16_t)ecc[2] << 6) & 0x3c0)];
-	par[3] = rs_decoder->index_of[(((uint16_t)ecc[2] >> 4) & 0x00f) |
-				      (((uint16_t)ecc[3] << 4) & 0x3f0)];
-	par[2] = rs_decoder->index_of[(((uint16_t)ecc[3] >> 6) & 0x003) |
-				      (((uint16_t)ecc[4] << 2) & 0x3fc)];
-	par[1] = rs_decoder->index_of[(((uint16_t)ecc[5] >> 0) & 0x0ff) |
-				      (((uint16_t)ecc[6] << 8) & 0x300)];
-	par[0] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0);
+	par[5] = rs_decoder->index_of[(((uint16_t) ecc[0] >> 0) & 0x0ff) | (((uint16_t) ecc[1] << 8) & 0x300)];
+	par[4] = rs_decoder->index_of[(((uint16_t) ecc[1] >> 2) & 0x03f) | (((uint16_t) ecc[2] << 6) & 0x3c0)];
+	par[3] = rs_decoder->index_of[(((uint16_t) ecc[2] >> 4) & 0x00f) | (((uint16_t) ecc[3] << 4) & 0x3f0)];
+	par[2] = rs_decoder->index_of[(((uint16_t) ecc[3] >> 6) & 0x003) | (((uint16_t) ecc[4] << 2) & 0x3fc)];
+	par[1] = rs_decoder->index_of[(((uint16_t) ecc[5] >> 0) & 0x0ff) | (((uint16_t) ecc[6] << 8) & 0x300)];
+	par[0] = (((uint16_t) ecc[6] >> 2) & 0x03f) | (((uint16_t) ecc[7] << 6) & 0x3c0);
 
 
 	/* Convert to computable syndrome */
 	/* Convert to computable syndrome */
 	for (i = 0; i < 6; i++) {
 	for (i = 0; i < 6; i++) {
@@ -453,16 +420,14 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
 		syn[i] = rs_decoder->index_of[syn[i]];
 		syn[i] = rs_decoder->index_of[syn[i]];
 	}
 	}
 
 
-	/* Let the library code do its magic.*/
-	res = decode_rs8(rs_decoder, (uint8_t *)buf, par, 512, syn, 0, NULL, 0xff, NULL);
+	/* Let the library code do its magic. */
+	res = decode_rs8(rs_decoder, (uint8_t *) buf, par, 512, syn, 0, NULL, 0xff, NULL);
 	if (res > 0) {
 	if (res > 0) {
-		DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: "
-			"ECC corrected %d errors on read\n", res);
+		DEBUG(MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " "ECC corrected %d errors on read\n", res);
 	}
 	}
 	return res;
 	return res;
 }
 }
 
 
-
 /**
 /**
  * rtc_from4_errstat - perform additional error status checks
  * rtc_from4_errstat - perform additional error status checks
  * @mtd:	MTD device structure
  * @mtd:	MTD device structure
@@ -478,54 +443,66 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
  * note: see pages 34..37 of data sheet for details.
  * note: see pages 34..37 of data sheet for details.
  *
  *
  */
  */
-static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page)
+static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this,
+			     int state, int status, int page)
 {
 {
-	int	er_stat=0;
-	int	rtn, retlen;
-	size_t	len;
+	int er_stat = 0;
+	int rtn, retlen;
+	size_t len;
 	uint8_t *buf;
 	uint8_t *buf;
-	int	i;
-
-	this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1);
-
-        if (state == FL_ERASING) {
-		for (i=0; i<4; i++) {
-			if (status & 1<<(i+1)) {
-				this->cmdfunc (mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1);
-				rtn = this->read_byte(mtd);
-				this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1);
-				if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
-					er_stat |= 1<<(i+1);	/* err_ecc_not_avail */
-				}
-			}
+	int i;
+
+	this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1);
+
+	if (state == FL_ERASING) {
+
+		for (i = 0; i < 4; i++) {
+			if (!(status & 1 << (i + 1)))
+				continue;
+			this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1),
+				      -1, -1);
+			rtn = this->read_byte(mtd);
+			this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
+
+			/* err_ecc_not_avail */
+			if (!(rtn & ERR_STAT_ECC_AVAILABLE))
+				er_stat |= 1 << (i + 1);
 		}
 		}
+
 	} else if (state == FL_WRITING) {
 	} else if (state == FL_WRITING) {
+
+		unsigned long corrected = mtd->ecc_stats.corrected;
+
 		/* single bank write logic */
 		/* single bank write logic */
-		this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1);
+		this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1);
 		rtn = this->read_byte(mtd);
 		rtn = this->read_byte(mtd);
-		this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1);
+		this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
+
 		if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
 		if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
-			er_stat |= 1<<1;	/* err_ecc_not_avail */
-		} else {
-			len = mtd->oobblock;
-			buf = kmalloc (len, GFP_KERNEL);
-			if (!buf) {
-				printk (KERN_ERR "rtc_from4_errstat: Out of memory!\n");
-				er_stat = 1;			/* if we can't check, assume failed */
-			} else {
-				/* recovery read */
-				/* page read */
-				rtn = nand_do_read_ecc (mtd, page, len, &retlen, buf, NULL, this->autooob, 1);
-				if (rtn) {	/* if read failed or > 1-bit error corrected */
-					er_stat |= 1<<1;	/* ECC read failed */
-				}
-				kfree(buf);
-			}
+			/* err_ecc_not_avail */
+			er_stat |= 1 << 1;
+			goto out;
+		}
+
+		len = mtd->writesize;
+		buf = kmalloc(len, GFP_KERNEL);
+		if (!buf) {
+			printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n");
+			er_stat = 1;
+			goto out;
 		}
 		}
+
+		/* recovery read */
+		rtn = nand_do_read(mtd, page, len, &retlen, buf);
+
+		/* if read failed or > 1-bit error corrected */
+		if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) {
+			er_stat |= 1 << 1;
+		kfree(buf);
 	}
 	}
 
 
 	rtn = status;
 	rtn = status;
-	if (er_stat == 0) {				/* if ECC is available   */
+	if (er_stat == 0) {	/* if ECC is available   */
 		rtn = (status & ~NAND_STATUS_FAIL);	/*   clear the error bit */
 		rtn = (status & ~NAND_STATUS_FAIL);	/*   clear the error bit */
 	}
 	}
 
 
@@ -533,33 +510,32 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int s
 }
 }
 #endif
 #endif
 
 
-
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-int __init rtc_from4_init (void)
+static int __init rtc_from4_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
 	unsigned short bcr1, bcr2, wcr2;
 	unsigned short bcr1, bcr2, wcr2;
 	int i;
 	int i;
 
 
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip),
-				GFP_KERNEL);
+	rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!rtc_from4_mtd) {
 	if (!rtc_from4_mtd) {
-		printk ("Unable to allocate Renesas NAND MTD device structure.\n");
+		printk("Unable to allocate Renesas NAND MTD device structure.\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&rtc_from4_mtd[1]);
+	this = (struct nand_chip *)(&rtc_from4_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(rtc_from4_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	rtc_from4_mtd->priv = this;
 	rtc_from4_mtd->priv = this;
+	rtc_from4_mtd->owner = THIS_MODULE;
 
 
 	/* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */
 	/* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */
 	bcr1 = *SH77X9_BCR1 & ~0x0002;
 	bcr1 = *SH77X9_BCR1 & ~0x0002;
@@ -580,9 +556,9 @@ int __init rtc_from4_init (void)
 	this->IO_ADDR_R = rtc_from4_fio_base;
 	this->IO_ADDR_R = rtc_from4_fio_base;
 	this->IO_ADDR_W = rtc_from4_fio_base;
 	this->IO_ADDR_W = rtc_from4_fio_base;
 	/* Set address of hardware control function */
 	/* Set address of hardware control function */
-	this->hwcontrol = rtc_from4_hwcontrol;
+	this->cmd_ctrl = rtc_from4_hwcontrol;
 	/* Set address of chip select function */
 	/* Set address of chip select function */
-        this->select_chip = rtc_from4_nand_select_chip;
+	this->select_chip = rtc_from4_nand_select_chip;
 	/* command delay time (in us) */
 	/* command delay time (in us) */
 	this->chip_delay = 100;
 	this->chip_delay = 100;
 	/* return the status of the Ready/Busy line */
 	/* return the status of the Ready/Busy line */
@@ -591,19 +567,20 @@ int __init rtc_from4_init (void)
 #ifdef RTC_FROM4_HWECC
 #ifdef RTC_FROM4_HWECC
 	printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n");
 	printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n");
 
 
-        this->eccmode = NAND_ECC_HW8_512;
-	this->options |= NAND_HWECC_SYNDROME;
+	this->ecc.mode = NAND_ECC_HW_SYNDROME;
+	this->ecc.size = 512;
+	this->ecc.bytes = 8;
 	/* return the status of extra status and ECC checks */
 	/* return the status of extra status and ECC checks */
 	this->errstat = rtc_from4_errstat;
 	this->errstat = rtc_from4_errstat;
 	/* set the nand_oobinfo to support FPGA H/W error detection */
 	/* set the nand_oobinfo to support FPGA H/W error detection */
-	this->autooob = &rtc_from4_nand_oobinfo;
-	this->enable_hwecc = rtc_from4_enable_hwecc;
-	this->calculate_ecc = rtc_from4_calculate_ecc;
-	this->correct_data = rtc_from4_correct_data;
+	this->ecc.layout = &rtc_from4_nand_oobinfo;
+	this->ecc.hwctl = rtc_from4_enable_hwecc;
+	this->ecc.calculate = rtc_from4_calculate_ecc;
+	this->ecc.correct = rtc_from4_correct_data;
 #else
 #else
 	printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n");
 	printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n");
 
 
-	this->eccmode = NAND_ECC_SOFT;
+	this->ecc.mode = NAND_ECC_SOFT;
 #endif
 #endif
 
 
 	/* set the bad block tables to support debugging */
 	/* set the bad block tables to support debugging */
@@ -617,7 +594,7 @@ int __init rtc_from4_init (void)
 	}
 	}
 
 
 	/* Perform 'device recovery' for each chip in case there was a power loss. */
 	/* Perform 'device recovery' for each chip in case there was a power loss. */
-	for (i=0; i < this->numchips; i++) {
+	for (i = 0; i < this->numchips; i++) {
 		deplete(rtc_from4_mtd, i);
 		deplete(rtc_from4_mtd, i);
 	}
 	}
 
 
@@ -643,7 +620,7 @@ int __init rtc_from4_init (void)
 	 */
 	 */
 	rs_decoder = init_rs(10, 0x409, 0, 1, 6);
 	rs_decoder = init_rs(10, 0x409, 0, 1, 6);
 	if (!rs_decoder) {
 	if (!rs_decoder) {
-		printk (KERN_ERR "Could not create a RS decoder\n");
+		printk(KERN_ERR "Could not create a RS decoder\n");
 		nand_release(rtc_from4_mtd);
 		nand_release(rtc_from4_mtd);
 		kfree(rtc_from4_mtd);
 		kfree(rtc_from4_mtd);
 		return -ENOMEM;
 		return -ENOMEM;
@@ -652,20 +629,19 @@ int __init rtc_from4_init (void)
 	/* Return happy */
 	/* Return happy */
 	return 0;
 	return 0;
 }
 }
-module_init(rtc_from4_init);
 
 
+module_init(rtc_from4_init);
 
 
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-#ifdef MODULE
-static void __exit rtc_from4_cleanup (void)
+static void __exit rtc_from4_cleanup(void)
 {
 {
 	/* Release resource, unregister partitions */
 	/* Release resource, unregister partitions */
 	nand_release(rtc_from4_mtd);
 	nand_release(rtc_from4_mtd);
 
 
 	/* Free the MTD device structure */
 	/* Free the MTD device structure */
-	kfree (rtc_from4_mtd);
+	kfree(rtc_from4_mtd);
 
 
 #ifdef RTC_FROM4_HWECC
 #ifdef RTC_FROM4_HWECC
 	/* Free the reed solomon resources */
 	/* Free the reed solomon resources */
@@ -674,10 +650,9 @@ static void __exit rtc_from4_cleanup (void)
 	}
 	}
 #endif
 #endif
 }
 }
+
 module_exit(rtc_from4_cleanup);
 module_exit(rtc_from4_cleanup);
-#endif
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("d.marlin <dmarlin@redhat.com");
 MODULE_AUTHOR("d.marlin <dmarlin@redhat.com");
 MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");
 MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");
-

+ 135 - 117
drivers/mtd/nand/s3c2410.c

@@ -18,8 +18,9 @@
  *	20-Jun-2005  BJD  Updated s3c2440 support, fixed timing bug
  *	20-Jun-2005  BJD  Updated s3c2440 support, fixed timing bug
  *	08-Jul-2005  BJD  Fix OOPS when no platform data supplied
  *	08-Jul-2005  BJD  Fix OOPS when no platform data supplied
  *	20-Oct-2005  BJD  Fix timing calculation bug
  *	20-Oct-2005  BJD  Fix timing calculation bug
+ *	14-Jan-2006  BJD  Allow clock to be stopped when idle
  *
  *
- * $Id: s3c2410.c,v 1.20 2005/11/07 11:14:31 gleixner Exp $
+ * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -36,9 +37,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 */
 
 
-#include <config/mtd/nand/s3c2410/hwecc.h>
-#include <config/mtd/nand/s3c2410/debug.h>
-
 #ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
 #ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
 #define DEBUG
 #define DEBUG
 #endif
 #endif
@@ -73,14 +71,20 @@ static int hardware_ecc = 1;
 static int hardware_ecc = 0;
 static int hardware_ecc = 0;
 #endif
 #endif
 
 
+#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
+static int clock_stop = 1;
+#else
+static const int clock_stop = 0;
+#endif
+
+
 /* new oob placement block for use with hardware ecc generation
 /* new oob placement block for use with hardware ecc generation
  */
  */
 
 
-static struct nand_oobinfo nand_hw_eccoob = {
-	.useecc		= MTD_NANDECC_AUTOPLACE,
-	.eccbytes	= 3,
-	.eccpos		= {0, 1, 2 },
-	.oobfree	= { {8, 8} }
+static struct nand_ecclayout nand_hw_eccoob = {
+	.eccbytes = 3,
+	.eccpos = {0, 1, 2},
+	.oobfree = {{8, 8}}
 };
 };
 
 
 /* controller and mtd information */
 /* controller and mtd information */
@@ -135,6 +139,11 @@ static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
 	return dev->dev.platform_data;
 	return dev->dev.platform_data;
 }
 }
 
 
+static inline int allow_clk_stop(struct s3c2410_nand_info *info)
+{
+	return clock_stop;
+}
+
 /* timing calculations */
 /* timing calculations */
 
 
 #define NS_IN_KHZ 1000000
 #define NS_IN_KHZ 1000000
@@ -149,8 +158,7 @@ static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
 	pr_debug("result %d from %ld, %d\n", result, clk, wanted);
 	pr_debug("result %d from %ld, %d\n", result, clk, wanted);
 
 
 	if (result > max) {
 	if (result > max) {
-		printk("%d ns is too big for current clock rate %ld\n",
-		       wanted, clk);
+		printk("%d ns is too big for current clock rate %ld\n", wanted, clk);
 		return -1;
 		return -1;
 	}
 	}
 
 
@@ -164,8 +172,7 @@ static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
 
 
 /* controller setup */
 /* controller setup */
 
 
-static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
-			       struct platform_device *pdev)
+static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_device *pdev)
 {
 {
 	struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
 	struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
 	unsigned long clkrate = clk_get_rate(info->clk);
 	unsigned long clkrate = clk_get_rate(info->clk);
@@ -177,7 +184,7 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
 	clkrate /= 1000;	/* turn clock into kHz for ease of use */
 	clkrate /= 1000;	/* turn clock into kHz for ease of use */
 
 
 	if (plat != NULL) {
 	if (plat != NULL) {
-		tacls  = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
+		tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
 		twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
 		twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
 		twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
 		twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
 	} else {
 	} else {
@@ -193,19 +200,22 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
 	}
 	}
 
 
 	printk(KERN_INFO PFX "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
 	printk(KERN_INFO PFX "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
-	       tacls, to_ns(tacls, clkrate),
-	       twrph0, to_ns(twrph0, clkrate),
-	       twrph1, to_ns(twrph1, clkrate));
+	       tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
 
 
 	if (!info->is_s3c2440) {
 	if (!info->is_s3c2440) {
-		cfg  = S3C2410_NFCONF_EN;
-		cfg |= S3C2410_NFCONF_TACLS(tacls-1);
-		cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
-		cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
+		cfg = S3C2410_NFCONF_EN;
+		cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
+		cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
+		cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
 	} else {
 	} else {
-		cfg   = S3C2440_NFCONF_TACLS(tacls-1);
-		cfg  |= S3C2440_NFCONF_TWRPH0(twrph0-1);
-		cfg  |= S3C2440_NFCONF_TWRPH1(twrph1-1);
+		cfg = S3C2440_NFCONF_TACLS(tacls - 1);
+		cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
+		cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
+
+		/* enable the controller and de-assert nFCE */
+
+		writel(S3C2440_NFCONT_ENABLE | S3C2440_NFCONT_ENABLE,
+		       info->regs + S3C2440_NFCONT);
 	}
 	}
 
 
 	pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
 	pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
@@ -229,7 +239,10 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
 	info = nmtd->info;
 	info = nmtd->info;
 
 
 	bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
 	bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
-	reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF);
+	reg = info->regs + ((info->is_s3c2440) ? S3C2440_NFCONT : S3C2410_NFCONF);
+
+	if (chip != -1 && allow_clk_stop(info))
+		clk_enable(info->clk);
 
 
 	cur = readl(reg);
 	cur = readl(reg);
 
 
@@ -243,77 +256,51 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
 
 
 		if (info->platform != NULL) {
 		if (info->platform != NULL) {
 			if (info->platform->select_chip != NULL)
 			if (info->platform->select_chip != NULL)
-				(info->platform->select_chip)(nmtd->set, chip);
+				(info->platform->select_chip) (nmtd->set, chip);
 		}
 		}
 
 
 		cur &= ~bit;
 		cur &= ~bit;
 	}
 	}
 
 
 	writel(cur, reg);
 	writel(cur, reg);
+
+	if (chip == -1 && allow_clk_stop(info))
+		clk_disable(info->clk);
 }
 }
 
 
-/* command and control functions
- *
- * Note, these all use tglx's method of changing the IO_ADDR_W field
- * to make the code simpler, and use the nand layer's code to issue the
- * command and address sequences via the proper IO ports.
+/* s3c2410_nand_hwcontrol
  *
  *
+ * Issue command and address cycles to the chip
 */
 */
 
 
-static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+				   unsigned int ctrl)
 {
 {
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-	struct nand_chip *chip = mtd->priv;
-
-	switch (cmd) {
-	case NAND_CTL_SETNCE:
-	case NAND_CTL_CLRNCE:
-		printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
-		break;
-
-	case NAND_CTL_SETCLE:
-		chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;
-		break;
-
-	case NAND_CTL_SETALE:
-		chip->IO_ADDR_W = info->regs + S3C2410_NFADDR;
-		break;
-
-		/* NAND_CTL_CLRCLE: */
-		/* NAND_CTL_CLRALE: */
-	default:
-		chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
-		break;
-	}
+	
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	if (ctrl & NAND_CLE)
+		writeb(cmd, info->regs + S3C2410_NFCMD);
+	else
+		writeb(cmd, info->regs + S3C2410_NFADDR);
 }
 }
 
 
 /* command and control functions */
 /* command and control functions */
 
 
-static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+				   unsigned int ctrl)
 {
 {
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-	struct nand_chip *chip = mtd->priv;
-
-	switch (cmd) {
-	case NAND_CTL_SETNCE:
-	case NAND_CTL_CLRNCE:
-		printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
-		break;
-
-	case NAND_CTL_SETCLE:
-		chip->IO_ADDR_W = info->regs + S3C2440_NFCMD;
-		break;
-
-	case NAND_CTL_SETALE:
-		chip->IO_ADDR_W = info->regs + S3C2440_NFADDR;
-		break;
-
-		/* NAND_CTL_CLRCLE: */
-		/* NAND_CTL_CLRALE: */
-	default:
-		chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
-		break;
-	}
+
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	if (ctrl & NAND_CLE)
+		writeb(cmd, info->regs + S3C2440_NFCMD);
+	else
+		writeb(cmd, info->regs + S3C2440_NFADDR);
 }
 }
 
 
 /* s3c2410_nand_devready()
 /* s3c2410_nand_devready()
@@ -330,22 +317,16 @@ static int s3c2410_nand_devready(struct mtd_info *mtd)
 	return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
 	return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
 }
 }
 
 
-
 /* ECC handling functions */
 /* ECC handling functions */
 
 
-static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
-				     u_char *read_ecc, u_char *calc_ecc)
+static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
 {
 {
-	pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n",
-		 mtd, dat, read_ecc, calc_ecc);
+	pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
 
 
 	pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
 	pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
-		 read_ecc[0], read_ecc[1], read_ecc[2],
-		 calc_ecc[0], calc_ecc[1], calc_ecc[2]);
+		 read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
 
 
-	if (read_ecc[0] == calc_ecc[0] &&
-	    read_ecc[1] == calc_ecc[1] &&
-	    read_ecc[2] == calc_ecc[2])
+	if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2])
 		return 0;
 		return 0;
 
 
 	/* we curently have no method for correcting the error */
 	/* we curently have no method for correcting the error */
@@ -378,8 +359,7 @@ static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
 	writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
 	writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
 }
 }
 
 
-static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
-				      const u_char *dat, u_char *ecc_code)
+static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
 {
 {
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 
 
@@ -387,15 +367,12 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
 	ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
 	ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
 	ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
 	ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
 
 
-	pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
-		 ecc_code[0], ecc_code[1], ecc_code[2]);
+	pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-
-static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd,
-				      const u_char *dat, u_char *ecc_code)
+static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
 {
 {
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 	unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
 	unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
@@ -404,13 +381,11 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd,
 	ecc_code[1] = ecc >> 8;
 	ecc_code[1] = ecc >> 8;
 	ecc_code[2] = ecc >> 16;
 	ecc_code[2] = ecc >> 16;
 
 
-	pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
-		 ecc_code[0], ecc_code[1], ecc_code[2]);
+	pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-
 /* over-ride the standard functions for a little more speed. We can
 /* over-ride the standard functions for a little more speed. We can
  * use read/write block to move the data buffers to/from the controller
  * use read/write block to move the data buffers to/from the controller
 */
 */
@@ -421,8 +396,7 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 	readsb(this->IO_ADDR_R, buf, len);
 	readsb(this->IO_ADDR_R, buf, len);
 }
 }
 
 
-static void s3c2410_nand_write_buf(struct mtd_info *mtd,
-				   const u_char *buf, int len)
+static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 {
 	struct nand_chip *this = mtd->priv;
 	struct nand_chip *this = mtd->priv;
 	writesb(this->IO_ADDR_W, buf, len);
 	writesb(this->IO_ADDR_W, buf, len);
@@ -459,7 +433,8 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
 	/* free the common resources */
 	/* free the common resources */
 
 
 	if (info->clk != NULL && !IS_ERR(info->clk)) {
 	if (info->clk != NULL && !IS_ERR(info->clk)) {
-		clk_disable(info->clk);
+		if (!allow_clk_stop(info))
+			clk_disable(info->clk);
 		clk_put(info->clk);
 		clk_put(info->clk);
 	}
 	}
 
 
@@ -488,9 +463,7 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
 		return add_mtd_device(&mtd->mtd);
 		return add_mtd_device(&mtd->mtd);
 
 
 	if (set->nr_partitions > 0 && set->partitions != NULL) {
 	if (set->nr_partitions > 0 && set->partitions != NULL) {
-		return add_mtd_partitions(&mtd->mtd,
-					  set->partitions,
-					  set->nr_partitions);
+		return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
 	}
 	}
 
 
 	return add_mtd_device(&mtd->mtd);
 	return add_mtd_device(&mtd->mtd);
@@ -517,7 +490,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
 
 
 	chip->IO_ADDR_R	   = info->regs + S3C2410_NFDATA;
 	chip->IO_ADDR_R	   = info->regs + S3C2410_NFDATA;
 	chip->IO_ADDR_W    = info->regs + S3C2410_NFDATA;
 	chip->IO_ADDR_W    = info->regs + S3C2410_NFDATA;
-	chip->hwcontrol    = s3c2410_nand_hwcontrol;
+	chip->cmd_ctrl     = s3c2410_nand_hwcontrol;
 	chip->dev_ready    = s3c2410_nand_devready;
 	chip->dev_ready    = s3c2410_nand_devready;
 	chip->write_buf    = s3c2410_nand_write_buf;
 	chip->write_buf    = s3c2410_nand_write_buf;
 	chip->read_buf     = s3c2410_nand_read_buf;
 	chip->read_buf     = s3c2410_nand_read_buf;
@@ -530,26 +503,29 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
 	if (info->is_s3c2440) {
 	if (info->is_s3c2440) {
 		chip->IO_ADDR_R	 = info->regs + S3C2440_NFDATA;
 		chip->IO_ADDR_R	 = info->regs + S3C2440_NFDATA;
 		chip->IO_ADDR_W  = info->regs + S3C2440_NFDATA;
 		chip->IO_ADDR_W  = info->regs + S3C2440_NFDATA;
-		chip->hwcontrol  = s3c2440_nand_hwcontrol;
+		chip->cmd_ctrl   = s3c2440_nand_hwcontrol;
 	}
 	}
 
 
 	nmtd->info	   = info;
 	nmtd->info	   = info;
 	nmtd->mtd.priv	   = chip;
 	nmtd->mtd.priv	   = chip;
+	nmtd->mtd.owner    = THIS_MODULE;
 	nmtd->set	   = set;
 	nmtd->set	   = set;
 
 
 	if (hardware_ecc) {
 	if (hardware_ecc) {
-		chip->correct_data  = s3c2410_nand_correct_data;
-		chip->enable_hwecc  = s3c2410_nand_enable_hwecc;
-		chip->calculate_ecc = s3c2410_nand_calculate_ecc;
-		chip->eccmode	    = NAND_ECC_HW3_512;
-		chip->autooob       = &nand_hw_eccoob;
+		chip->ecc.correct   = s3c2410_nand_correct_data;
+		chip->ecc.hwctl	    = s3c2410_nand_enable_hwecc;
+		chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+		chip->ecc.mode	    = NAND_ECC_HW;
+		chip->ecc.size	    = 512;
+		chip->ecc.bytes	    = 3;
+		chip->ecc.layout    = &nand_hw_eccoob;
 
 
 		if (info->is_s3c2440) {
 		if (info->is_s3c2440) {
-			chip->enable_hwecc  = s3c2440_nand_enable_hwecc;
-			chip->calculate_ecc = s3c2440_nand_calculate_ecc;
+			chip->ecc.hwctl     = s3c2440_nand_enable_hwecc;
+			chip->ecc.calculate = s3c2440_nand_calculate_ecc;
 		}
 		}
 	} else {
 	} else {
-		chip->eccmode	    = NAND_ECC_SOFT;
+		chip->ecc.mode	    = NAND_ECC_SOFT;
 	}
 	}
 }
 }
 
 
@@ -654,13 +630,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
 	nmtd = info->mtds;
 	nmtd = info->mtds;
 
 
 	for (setno = 0; setno < nr_sets; setno++, nmtd++) {
 	for (setno = 0; setno < nr_sets; setno++, nmtd++) {
-		pr_debug("initialising set %d (%p, info %p)\n",
-			 setno, nmtd, info);
+		pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
 
 
 		s3c2410_nand_init_chip(info, nmtd, sets);
 		s3c2410_nand_init_chip(info, nmtd, sets);
 
 
-		nmtd->scan_res = nand_scan(&nmtd->mtd,
-					   (sets) ? sets->nr_chips : 1);
+		nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
 
 
 		if (nmtd->scan_res == 0) {
 		if (nmtd->scan_res == 0) {
 			s3c2410_nand_add_partition(info, nmtd, sets);
 			s3c2410_nand_add_partition(info, nmtd, sets);
@@ -670,6 +644,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
 			sets++;
 			sets++;
 	}
 	}
 
 
+	if (allow_clk_stop(info)) {
+		dev_info(&pdev->dev, "clock idle support enabled\n");
+		clk_disable(info->clk);
+	}
+
 	pr_debug("initialised ok\n");
 	pr_debug("initialised ok\n");
 	return 0;
 	return 0;
 
 
@@ -681,6 +660,41 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
 	return err;
 	return err;
 }
 }
 
 
+/* PM Support */
+#ifdef CONFIG_PM
+
+static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
+{
+	struct s3c2410_nand_info *info = platform_get_drvdata(dev);
+
+	if (info) {
+		if (!allow_clk_stop(info))
+			clk_disable(info->clk);
+	}
+
+	return 0;
+}
+
+static int s3c24xx_nand_resume(struct platform_device *dev)
+{
+	struct s3c2410_nand_info *info = platform_get_drvdata(dev);
+
+	if (info) {
+		clk_enable(info->clk);
+		s3c2410_nand_inithw(info, dev);
+
+		if (allow_clk_stop(info))
+			clk_disable(info->clk);
+	}
+
+	return 0;
+}
+
+#else
+#define s3c24xx_nand_suspend NULL
+#define s3c24xx_nand_resume NULL
+#endif
+
 /* driver device registration */
 /* driver device registration */
 
 
 static int s3c2410_nand_probe(struct platform_device *dev)
 static int s3c2410_nand_probe(struct platform_device *dev)
@@ -696,6 +710,8 @@ static int s3c2440_nand_probe(struct platform_device *dev)
 static struct platform_driver s3c2410_nand_driver = {
 static struct platform_driver s3c2410_nand_driver = {
 	.probe		= s3c2410_nand_probe,
 	.probe		= s3c2410_nand_probe,
 	.remove		= s3c2410_nand_remove,
 	.remove		= s3c2410_nand_remove,
+	.suspend	= s3c24xx_nand_suspend,
+	.resume		= s3c24xx_nand_resume,
 	.driver		= {
 	.driver		= {
 		.name	= "s3c2410-nand",
 		.name	= "s3c2410-nand",
 		.owner	= THIS_MODULE,
 		.owner	= THIS_MODULE,
@@ -705,6 +721,8 @@ static struct platform_driver s3c2410_nand_driver = {
 static struct platform_driver s3c2440_nand_driver = {
 static struct platform_driver s3c2440_nand_driver = {
 	.probe		= s3c2440_nand_probe,
 	.probe		= s3c2440_nand_probe,
 	.remove		= s3c2410_nand_remove,
 	.remove		= s3c2410_nand_remove,
+	.suspend	= s3c24xx_nand_suspend,
+	.resume		= s3c24xx_nand_resume,
 	.driver		= {
 	.driver		= {
 		.name	= "s3c2440-nand",
 		.name	= "s3c2440-nand",
 		.owner	= THIS_MODULE,
 		.owner	= THIS_MODULE,

+ 66 - 80
drivers/mtd/nand/sharpsl.c

@@ -46,7 +46,6 @@ static int sharpsl_phys_base = 0x0C000000;
 #define FLCLE		(1 << 1)
 #define FLCLE		(1 << 1)
 #define FLCE0		(1 << 0)
 #define FLCE0		(1 << 0)
 
 
-
 /*
 /*
  * MTD structure for SharpSL
  * MTD structure for SharpSL
  */
  */
@@ -60,50 +59,44 @@ static struct mtd_info *sharpsl_mtd = NULL;
 static int nr_partitions;
 static int nr_partitions;
 static struct mtd_partition sharpsl_nand_default_partition_info[] = {
 static struct mtd_partition sharpsl_nand_default_partition_info[] = {
 	{
 	{
-	.name = "System Area",
-	.offset = 0,
-	.size = 7 * 1024 * 1024,
-	},
+	 .name = "System Area",
+	 .offset = 0,
+	 .size = 7 * 1024 * 1024,
+	 },
 	{
 	{
-	.name = "Root Filesystem",
-	.offset = 7 * 1024 * 1024,
-	.size = 30 * 1024 * 1024,
-	},
+	 .name = "Root Filesystem",
+	 .offset = 7 * 1024 * 1024,
+	 .size = 30 * 1024 * 1024,
+	 },
 	{
 	{
-	.name = "Home Filesystem",
-	.offset = MTDPART_OFS_APPEND ,
-	.size = MTDPART_SIZ_FULL ,
-	},
+	 .name = "Home Filesystem",
+	 .offset = MTDPART_OFS_APPEND,
+	 .size = MTDPART_SIZ_FULL,
+	 },
 };
 };
 
 
 /*
 /*
  *	hardware specific access to control-lines
  *	hardware specific access to control-lines
+ *	ctrl:
+ *	NAND_CNE: bit 0 -> bit 0 & 4
+ *	NAND_CLE: bit 1 -> bit 1
+ *	NAND_ALE: bit 2 -> bit 2
+ *
  */
  */
-static void
-sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd)
+static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+				   unsigned int ctrl)
 {
 {
-	switch (cmd) {
-	case NAND_CTL_SETCLE:
-		writeb(readb(FLASHCTL) | FLCLE, FLASHCTL);
-		break;
-	case NAND_CTL_CLRCLE:
-		writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL);
-		break;
-
-	case NAND_CTL_SETALE:
-		writeb(readb(FLASHCTL) | FLALE, FLASHCTL);
-		break;
-	case NAND_CTL_CLRALE:
-		writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL);
-		break;
-
-	case NAND_CTL_SETNCE:
-		writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL);
-		break;
-	case NAND_CTL_CLRNCE:
-		writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL);
-		break;
+	struct nand_chip *chip = mtd->priv;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned char bits = ctrl & 0x07;
+
+		bits |= (ctrl & 0x01) << 4;
+		writeb((readb(FLASHCTL) & 0x17) | bits, FLASHCTL);
 	}
 	}
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 }
 
 
 static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
 static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
@@ -122,31 +115,26 @@ static struct nand_bbt_descr sharpsl_akita_bbt = {
 	.pattern = scan_ff_pattern
 	.pattern = scan_ff_pattern
 };
 };
 
 
-static struct nand_oobinfo akita_oobinfo = {
-	.useecc = MTD_NANDECC_AUTOPLACE,
+static struct nand_ecclayout akita_oobinfo = {
 	.eccbytes = 24,
 	.eccbytes = 24,
 	.eccpos = {
 	.eccpos = {
-		0x5,  0x1,  0x2,  0x3,  0x6,  0x7,  0x15, 0x11,
-		0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
-		0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37},
-	.oobfree = { {0x08, 0x09} }
+		   0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11,
+		   0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
+		   0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37},
+	.oobfree = {{0x08, 0x09}}
 };
 };
 
 
-static int
-sharpsl_nand_dev_ready(struct mtd_info* mtd)
+static int sharpsl_nand_dev_ready(struct mtd_info *mtd)
 {
 {
 	return !((readb(FLASHCTL) & FLRYBY) == 0);
 	return !((readb(FLASHCTL) & FLRYBY) == 0);
 }
 }
 
 
-static void
-sharpsl_nand_enable_hwecc(struct mtd_info* mtd, int mode)
+static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode)
 {
 {
-	writeb(0 ,ECCCLRR);
+	writeb(0, ECCCLRR);
 }
 }
 
 
-static int
-sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat,
-				u_char* ecc_code)
+static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code)
 {
 {
 	ecc_code[0] = ~readb(ECCLPUB);
 	ecc_code[0] = ~readb(ECCLPUB);
 	ecc_code[1] = ~readb(ECCLPLB);
 	ecc_code[1] = ~readb(ECCLPLB);
@@ -154,47 +142,44 @@ sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat,
 	return readb(ECCCNTR) != 0;
 	return readb(ECCCNTR) != 0;
 }
 }
 
 
-
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_PARTITIONS
 const char *part_probes[] = { "cmdlinepart", NULL };
 const char *part_probes[] = { "cmdlinepart", NULL };
 #endif
 #endif
 
 
-
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-int __init
-sharpsl_nand_init(void)
+static int __init sharpsl_nand_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
-	struct mtd_partition* sharpsl_partition_info;
+	struct mtd_partition *sharpsl_partition_info;
 	int err = 0;
 	int err = 0;
 
 
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
-				GFP_KERNEL);
+	sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!sharpsl_mtd) {
 	if (!sharpsl_mtd) {
-		printk ("Unable to allocate SharpSL NAND MTD device structure.\n");
+		printk("Unable to allocate SharpSL NAND MTD device structure.\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
 	/* map physical adress */
 	/* map physical adress */
 	sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000);
 	sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000);
-	if(!sharpsl_io_base){
+	if (!sharpsl_io_base) {
 		printk("ioremap to access Sharp SL NAND chip failed\n");
 		printk("ioremap to access Sharp SL NAND chip failed\n");
 		kfree(sharpsl_mtd);
 		kfree(sharpsl_mtd);
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&sharpsl_mtd[1]);
+	this = (struct nand_chip *)(&sharpsl_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(sharpsl_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	sharpsl_mtd->priv = this;
 	sharpsl_mtd->priv = this;
+	sharpsl_mtd->owner = THIS_MODULE;
 
 
 	/*
 	/*
 	 * PXA initialize
 	 * PXA initialize
@@ -205,23 +190,25 @@ sharpsl_nand_init(void)
 	this->IO_ADDR_R = FLASHIO;
 	this->IO_ADDR_R = FLASHIO;
 	this->IO_ADDR_W = FLASHIO;
 	this->IO_ADDR_W = FLASHIO;
 	/* Set address of hardware control function */
 	/* Set address of hardware control function */
-	this->hwcontrol = sharpsl_nand_hwcontrol;
+	this->cmd_ctrl = sharpsl_nand_hwcontrol;
 	this->dev_ready = sharpsl_nand_dev_ready;
 	this->dev_ready = sharpsl_nand_dev_ready;
 	/* 15 us command delay time */
 	/* 15 us command delay time */
 	this->chip_delay = 15;
 	this->chip_delay = 15;
 	/* set eccmode using hardware ECC */
 	/* set eccmode using hardware ECC */
-	this->eccmode = NAND_ECC_HW3_256;
+	this->ecc.mode = NAND_ECC_HW;
+	this->ecc.size = 256;
+	this->ecc.bytes = 3;
 	this->badblock_pattern = &sharpsl_bbt;
 	this->badblock_pattern = &sharpsl_bbt;
 	if (machine_is_akita() || machine_is_borzoi()) {
 	if (machine_is_akita() || machine_is_borzoi()) {
 		this->badblock_pattern = &sharpsl_akita_bbt;
 		this->badblock_pattern = &sharpsl_akita_bbt;
-		this->autooob = &akita_oobinfo;
+		this->ecc.layout = &akita_oobinfo;
 	}
 	}
-	this->enable_hwecc = sharpsl_nand_enable_hwecc;
-	this->calculate_ecc = sharpsl_nand_calculate_ecc;
-	this->correct_data = nand_correct_data;
+	this->ecc.hwctl = sharpsl_nand_enable_hwecc;
+	this->ecc.calculate = sharpsl_nand_calculate_ecc;
+	this->ecc.correct = nand_correct_data;
 
 
 	/* Scan to find existence of the device */
 	/* Scan to find existence of the device */
-	err=nand_scan(sharpsl_mtd,1);
+	err = nand_scan(sharpsl_mtd, 1);
 	if (err) {
 	if (err) {
 		iounmap(sharpsl_io_base);
 		iounmap(sharpsl_io_base);
 		kfree(sharpsl_mtd);
 		kfree(sharpsl_mtd);
@@ -230,24 +217,23 @@ sharpsl_nand_init(void)
 
 
 	/* Register the partitions */
 	/* Register the partitions */
 	sharpsl_mtd->name = "sharpsl-nand";
 	sharpsl_mtd->name = "sharpsl-nand";
-	nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes,
-						&sharpsl_partition_info, 0);
+	nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, &sharpsl_partition_info, 0);
 
 
 	if (nr_partitions <= 0) {
 	if (nr_partitions <= 0) {
 		nr_partitions = DEFAULT_NUM_PARTITIONS;
 		nr_partitions = DEFAULT_NUM_PARTITIONS;
 		sharpsl_partition_info = sharpsl_nand_default_partition_info;
 		sharpsl_partition_info = sharpsl_nand_default_partition_info;
 		if (machine_is_poodle()) {
 		if (machine_is_poodle()) {
-			sharpsl_partition_info[1].size=30 * 1024 * 1024;
+			sharpsl_partition_info[1].size = 22 * 1024 * 1024;
 		} else if (machine_is_corgi() || machine_is_shepherd()) {
 		} else if (machine_is_corgi() || machine_is_shepherd()) {
-			sharpsl_partition_info[1].size=25 * 1024 * 1024;
+			sharpsl_partition_info[1].size = 25 * 1024 * 1024;
 		} else if (machine_is_husky()) {
 		} else if (machine_is_husky()) {
-			sharpsl_partition_info[1].size=53 * 1024 * 1024;
+			sharpsl_partition_info[1].size = 53 * 1024 * 1024;
 		} else if (machine_is_spitz()) {
 		} else if (machine_is_spitz()) {
-			sharpsl_partition_info[1].size=5 * 1024 * 1024;
+			sharpsl_partition_info[1].size = 5 * 1024 * 1024;
 		} else if (machine_is_akita()) {
 		} else if (machine_is_akita()) {
-			sharpsl_partition_info[1].size=58 * 1024 * 1024;
+			sharpsl_partition_info[1].size = 58 * 1024 * 1024;
 		} else if (machine_is_borzoi()) {
 		} else if (machine_is_borzoi()) {
-			sharpsl_partition_info[1].size=32 * 1024 * 1024;
+			sharpsl_partition_info[1].size = 32 * 1024 * 1024;
 		}
 		}
 	}
 	}
 
 
@@ -261,15 +247,15 @@ sharpsl_nand_init(void)
 	/* Return happy */
 	/* Return happy */
 	return 0;
 	return 0;
 }
 }
+
 module_init(sharpsl_nand_init);
 module_init(sharpsl_nand_init);
 
 
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-#ifdef MODULE
 static void __exit sharpsl_nand_cleanup(void)
 static void __exit sharpsl_nand_cleanup(void)
 {
 {
-	struct nand_chip *this = (struct nand_chip *) &sharpsl_mtd[1];
+	struct nand_chip *this = (struct nand_chip *)&sharpsl_mtd[1];
 
 
 	/* Release resources, unregister device */
 	/* Release resources, unregister device */
 	nand_release(sharpsl_mtd);
 	nand_release(sharpsl_mtd);
@@ -279,8 +265,8 @@ static void __exit sharpsl_nand_cleanup(void)
 	/* Free the MTD device structure */
 	/* Free the MTD device structure */
 	kfree(sharpsl_mtd);
 	kfree(sharpsl_mtd);
 }
 }
+
 module_exit(sharpsl_nand_cleanup);
 module_exit(sharpsl_nand_cleanup);
-#endif
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
 MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");

+ 53 - 48
drivers/mtd/nand/spia.c

@@ -39,16 +39,16 @@ static struct mtd_info *spia_mtd = NULL;
  */
  */
 #define SPIA_IO_BASE	0xd0000000	/* Start of EP7212 IO address space */
 #define SPIA_IO_BASE	0xd0000000	/* Start of EP7212 IO address space */
 #define SPIA_FIO_BASE	0xf0000000	/* Address where flash is mapped */
 #define SPIA_FIO_BASE	0xf0000000	/* Address where flash is mapped */
-#define SPIA_PEDR	0x0080		/*
-					 * IO offset to Port E data register
-					 * where the CLE, ALE and NCE pins
-					 * are wired to.
-					 */
-#define SPIA_PEDDR	0x00c0		/*
-					 * IO offset to Port E data direction
-					 * register so we can control the IO
-					 * lines.
-					 */
+#define SPIA_PEDR	0x0080	/*
+				 * IO offset to Port E data register
+				 * where the CLE, ALE and NCE pins
+				 * are wired to.
+				 */
+#define SPIA_PEDDR	0x00c0	/*
+				 * IO offset to Port E data direction
+				 * register so we can control the IO
+				 * lines.
+				 */
 
 
 /*
 /*
  * Module stuff
  * Module stuff
@@ -69,79 +69,84 @@ module_param(spia_peddr, int, 0);
  */
  */
 static const struct mtd_partition partition_info[] = {
 static const struct mtd_partition partition_info[] = {
 	{
 	{
-		.name	= "SPIA flash partition 1",
-		.offset	= 0,
-		.size	= 2*1024*1024
-	},
+	 .name = "SPIA flash partition 1",
+	 .offset = 0,
+	 .size = 2 * 1024 * 1024},
 	{
 	{
-		.name	= "SPIA flash partition 2",
-		.offset	= 2*1024*1024,
-		.size	= 6*1024*1024
-	}
+	 .name = "SPIA flash partition 2",
+	 .offset = 2 * 1024 * 1024,
+	 .size = 6 * 1024 * 1024}
 };
 };
-#define NUM_PARTITIONS 2
 
 
+#define NUM_PARTITIONS 2
 
 
 /*
 /*
  *	hardware specific access to control-lines
  *	hardware specific access to control-lines
-*/
-static void spia_hwcontrol(struct mtd_info *mtd, int cmd){
-
-    switch(cmd){
+ *
+ *	ctrl:
+ *	NAND_CNE: bit 0 -> bit 2
+ *	NAND_CLE: bit 1 -> bit 0
+ *	NAND_ALE: bit 2 -> bit 1
+ */
+static void spia_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+	struct nand_chip *chip = mtd->priv;
 
 
-	case NAND_CTL_SETCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |=  0x01; break;
-	case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x01; break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		void __iomem *addr = spia_io_base + spia_pedr;
+		unsigned char bits;
 
 
-	case NAND_CTL_SETALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |=  0x02; break;
-	case NAND_CTL_CLRALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x02; break;
+		bits = (ctrl & NAND_CNE) << 2;
+		bits |= (ctrl & NAND_CLE | NAND_ALE) >> 1;
+		writeb((readb(addr) & ~0x7) | bits, addr);
+	}
 
 
-	case NAND_CTL_SETNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x04; break;
-	case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |=  0x04; break;
-    }
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 }
 
 
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-int __init spia_init (void)
+static int __init spia_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
 
 
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	spia_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
-				GFP_KERNEL);
+	spia_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!spia_mtd) {
 	if (!spia_mtd) {
-		printk ("Unable to allocate SPIA NAND MTD device structure.\n");
+		printk("Unable to allocate SPIA NAND MTD device structure.\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&spia_mtd[1]);
+	this = (struct nand_chip *)(&spia_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) spia_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(spia_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	spia_mtd->priv = this;
 	spia_mtd->priv = this;
+	spia_mtd->owner = THIS_MODULE;
 
 
 	/*
 	/*
 	 * Set GPIO Port E control register so that the pins are configured
 	 * Set GPIO Port E control register so that the pins are configured
 	 * to be outputs for controlling the NAND flash.
 	 * to be outputs for controlling the NAND flash.
 	 */
 	 */
-	(*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07;
+	(*(volatile unsigned char *)(spia_io_base + spia_peddr)) = 0x07;
 
 
 	/* Set address of NAND IO lines */
 	/* Set address of NAND IO lines */
-	this->IO_ADDR_R = (void __iomem *) spia_fio_base;
-	this->IO_ADDR_W = (void __iomem *) spia_fio_base;
+	this->IO_ADDR_R = (void __iomem *)spia_fio_base;
+	this->IO_ADDR_W = (void __iomem *)spia_fio_base;
 	/* Set address of hardware control function */
 	/* Set address of hardware control function */
-	this->hwcontrol = spia_hwcontrol;
+	this->cmd_ctrl = spia_hwcontrol;
 	/* 15 us command delay time */
 	/* 15 us command delay time */
 	this->chip_delay = 15;
 	this->chip_delay = 15;
 
 
 	/* Scan to find existence of the device */
 	/* Scan to find existence of the device */
-	if (nand_scan (spia_mtd, 1)) {
-		kfree (spia_mtd);
+	if (nand_scan(spia_mtd, 1)) {
+		kfree(spia_mtd);
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
 
 
@@ -151,22 +156,22 @@ int __init spia_init (void)
 	/* Return happy */
 	/* Return happy */
 	return 0;
 	return 0;
 }
 }
+
 module_init(spia_init);
 module_init(spia_init);
 
 
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-#ifdef MODULE
-static void __exit spia_cleanup (void)
+static void __exit spia_cleanup(void)
 {
 {
 	/* Release resources, unregister device */
 	/* Release resources, unregister device */
-	nand_release (spia_mtd);
+	nand_release(spia_mtd);
 
 
 	/* Free the MTD device structure */
 	/* Free the MTD device structure */
-	kfree (spia_mtd);
+	kfree(spia_mtd);
 }
 }
+
 module_exit(spia_cleanup);
 module_exit(spia_cleanup);
-#endif
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");
 MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");

+ 62 - 59
drivers/mtd/nand/toto.c

@@ -32,6 +32,8 @@
 #include <asm/arch-omap1510/hardware.h>
 #include <asm/arch-omap1510/hardware.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/gpio.h>
 
 
+#define CONFIG_NAND_WORKAROUND 1
+
 /*
 /*
  * MTD structure for TOTO board
  * MTD structure for TOTO board
  */
  */
@@ -39,25 +41,6 @@ static struct mtd_info *toto_mtd = NULL;
 
 
 static unsigned long toto_io_base = OMAP_FLASH_1_BASE;
 static unsigned long toto_io_base = OMAP_FLASH_1_BASE;
 
 
-#define CONFIG_NAND_WORKAROUND 1
-
-#define NAND_NCE 0x4000
-#define NAND_CLE 0x1000
-#define NAND_ALE 0x0002
-#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
-
-#define T_NAND_CTL_CLRALE(iob)  gpiosetout(NAND_ALE, 0)
-#define T_NAND_CTL_SETALE(iob)  gpiosetout(NAND_ALE, NAND_ALE)
-#ifdef CONFIG_NAND_WORKAROUND     /* "some" dev boards busted, blue wired to rts2 :( */
-#define T_NAND_CTL_CLRCLE(iob)  gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
-#define T_NAND_CTL_SETCLE(iob)  gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
-#else
-#define T_NAND_CTL_CLRCLE(iob)  gpiosetout(NAND_CLE, 0)
-#define T_NAND_CTL_SETCLE(iob)  gpiosetout(NAND_CLE, NAND_CLE)
-#endif
-#define T_NAND_CTL_SETNCE(iob)  gpiosetout(NAND_NCE, 0)
-#define T_NAND_CTL_CLRNCE(iob)  gpiosetout(NAND_NCE, NAND_NCE)
-
 /*
 /*
  * Define partitions for flash devices
  * Define partitions for flash devices
  */
  */
@@ -91,91 +74,110 @@ static struct mtd_partition partition_info32M[] = {
 
 
 #define NUM_PARTITIONS32M 3
 #define NUM_PARTITIONS32M 3
 #define NUM_PARTITIONS64M 4
 #define NUM_PARTITIONS64M 4
+
 /*
 /*
  *	hardware specific access to control-lines
  *	hardware specific access to control-lines
-*/
-
-static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
+ *
+ *	ctrl:
+ *	NAND_NCE: bit 0 -> bit 14 (0x4000)
+ *	NAND_CLE: bit 1 -> bit 12 (0x1000)
+ *	NAND_ALE: bit 2 -> bit 1  (0x0002)
+ */
+static void toto_hwcontrol(struct mtd_info *mtd, int cmd,
+			   unsigned int ctrl)
 {
 {
+	struct nand_chip *chip = mtd->priv;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned long bits;
 
 
-	udelay(1); /* hopefully enough time for tc make proceding write to clear */
-	switch(cmd){
+		/* hopefully enough time for tc make proceding write to clear */
+		udelay(1);
 
 
-		case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
-		case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
+		bits = (~ctrl & NAND_NCE) << 14;
+		bits |= (ctrl & NAND_CLE) << 12;
+		bits |= (ctrl & NAND_ALE) >> 1;
 
 
-		case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
-		case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
+#warning Wild guess as gpiosetout() is nowhere defined in the kernel source - tglx
+		gpiosetout(0x5002, bits);
 
 
-		case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
-		case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
+#ifdef CONFIG_NAND_WORKAROUND
+		/* "some" dev boards busted, blue wired to rts2 :( */
+		rts2setout(2, (ctrl & NAND_CLE) << 1);
+#endif
+		/* allow time to ensure gpio state to over take memory write */
+		udelay(1);
 	}
 	}
-	udelay(1); /* allow time to ensure gpio state to over take memory write */
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 }
 
 
 /*
 /*
  * Main initialization routine
  * Main initialization routine
  */
  */
-int __init toto_init (void)
+static int __init toto_init(void)
 {
 {
 	struct nand_chip *this;
 	struct nand_chip *this;
 	int err = 0;
 	int err = 0;
 
 
 	/* Allocate memory for MTD device structure and private data */
 	/* Allocate memory for MTD device structure and private data */
-	toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
-				GFP_KERNEL);
+	toto_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 	if (!toto_mtd) {
 	if (!toto_mtd) {
-		printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
+		printk(KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto out;
 		goto out;
 	}
 	}
 
 
 	/* Get pointer to private data */
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&toto_mtd[1]);
+	this = (struct nand_chip *)(&toto_mtd[1]);
 
 
 	/* Initialize structures */
 	/* Initialize structures */
-	memset((char *) toto_mtd, 0, sizeof(struct mtd_info));
-	memset((char *) this, 0, sizeof(struct nand_chip));
+	memset(toto_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
 
 
 	/* Link the private data with the MTD structure */
 	/* Link the private data with the MTD structure */
 	toto_mtd->priv = this;
 	toto_mtd->priv = this;
+	toto_mtd->owner = THIS_MODULE;
 
 
 	/* Set address of NAND IO lines */
 	/* Set address of NAND IO lines */
 	this->IO_ADDR_R = toto_io_base;
 	this->IO_ADDR_R = toto_io_base;
 	this->IO_ADDR_W = toto_io_base;
 	this->IO_ADDR_W = toto_io_base;
-	this->hwcontrol = toto_hwcontrol;
+	this->cmd_ctrl = toto_hwcontrol;
 	this->dev_ready = NULL;
 	this->dev_ready = NULL;
 	/* 25 us command delay time */
 	/* 25 us command delay time */
 	this->chip_delay = 30;
 	this->chip_delay = 30;
-	this->eccmode = NAND_ECC_SOFT;
+	this->ecc.mode = NAND_ECC_SOFT;
 
 
-        /* Scan to find existance of the device */
-	if (nand_scan (toto_mtd, 1)) {
+	/* Scan to find existance of the device */
+	if (nand_scan(toto_mtd, 1)) {
 		err = -ENXIO;
 		err = -ENXIO;
 		goto out_mtd;
 		goto out_mtd;
 	}
 	}
 
 
 	/* Register the partitions */
 	/* Register the partitions */
-	switch(toto_mtd->size){
-		case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
-		case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break;
-		default: {
-			printk (KERN_WARNING "Unsupported Nand device\n");
+	switch (toto_mtd->size) {
+	case SZ_64M:
+		add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M);
+		break;
+	case SZ_32M:
+		add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M);
+		break;
+	default:{
+			printk(KERN_WARNING "Unsupported Nand device\n");
 			err = -ENXIO;
 			err = -ENXIO;
 			goto out_buf;
 			goto out_buf;
 		}
 		}
 	}
 	}
 
 
-    	gpioreserve(NAND_MASK);  /* claim our gpios */
-    	archflashwp(0,0);	 /* open up flash for writing */
+	gpioreserve(NAND_MASK);	/* claim our gpios */
+	archflashwp(0, 0);	/* open up flash for writing */
 
 
 	goto out;
 	goto out;
 
 
-out_buf:
-	kfree (this->data_buf);
-out_mtd:
-	kfree (toto_mtd);
-out:
+ out_mtd:
+	kfree(toto_mtd);
+ out:
 	return err;
 	return err;
 }
 }
 
 
@@ -184,20 +186,21 @@ module_init(toto_init);
 /*
 /*
  * Clean up routine
  * Clean up routine
  */
  */
-static void __exit toto_cleanup (void)
+static void __exit toto_cleanup(void)
 {
 {
 	/* Release resources, unregister device */
 	/* Release resources, unregister device */
-	nand_release (toto_mtd);
+	nand_release(toto_mtd);
 
 
 	/* Free the MTD device structure */
 	/* Free the MTD device structure */
-	kfree (toto_mtd);
+	kfree(toto_mtd);
 
 
 	/* stop flash writes */
 	/* stop flash writes */
-	 archflashwp(0,1);
+	archflashwp(0, 1);
 
 
 	/* release gpios to system */
 	/* release gpios to system */
-	 gpiorelease(NAND_MASK);
+	gpiorelease(NAND_MASK);
 }
 }
+
 module_exit(toto_cleanup);
 module_exit(toto_cleanup);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 206 - 0
drivers/mtd/nand/ts7250.c

@@ -0,0 +1,206 @@
+/*
+ * drivers/mtd/nand/ts7250.c
+ *
+ * Copyright (C) 2004 Technologic Systems (support@embeddedARM.com)
+ *
+ * Derived from drivers/mtd/nand/edb7312.c
+ *   Copyright (C) 2004 Marius Gröger (mag@sysgo.de)
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c
+ *   Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ *   This is a device driver for the NAND flash device found on the
+ *   TS-7250 board which utilizes a Samsung 32 Mbyte part.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/sizes.h>
+#include <asm/mach-types.h>
+
+/*
+ * MTD structure for TS7250 board
+ */
+static struct mtd_info *ts7250_mtd = NULL;
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL };
+
+#define NUM_PARTITIONS 3
+
+/*
+ * Define static partitions for flash device
+ */
+static struct mtd_partition partition_info32[] = {
+	{
+		.name		= "TS-BOOTROM",
+		.offset		= 0x00000000,
+		.size		= 0x00004000,
+	}, {
+		.name		= "Linux",
+		.offset		= 0x00004000,
+		.size		= 0x01d00000,
+	}, {
+		.name		= "RedBoot",
+		.offset		= 0x01d04000,
+		.size		= 0x002fc000,
+	},
+};
+
+/*
+ * Define static partitions for flash device
+ */
+static struct mtd_partition partition_info128[] = {
+	{
+		.name		= "TS-BOOTROM",
+		.offset		= 0x00000000,
+		.size		= 0x00004000,
+	}, {
+		.name		= "Linux",
+		.offset		= 0x00004000,
+		.size		= 0x07d00000,
+	}, {
+		.name		= "RedBoot",
+		.offset		= 0x07d04000,
+		.size		= 0x002fc000,
+	},
+};
+#endif
+
+
+/*
+ *	hardware specific access to control-lines
+ *
+ *	ctrl:
+ *	NAND_NCE: bit 0 -> bit 2
+ *	NAND_CLE: bit 1 -> bit 1
+ *	NAND_ALE: bit 2 -> bit 0
+ */
+static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE;
+		unsigned char bits;
+
+		bits = (ctrl & NAND_CNE) << 2;
+		bits |= ctrl & NAND_CLE;
+		bits |= (ctrl & NAND_ALE) >> 2;
+
+		__raw_writeb((__raw_readb(addr) & ~0x7) | bits, addr);
+	}
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
+}
+
+/*
+ *	read device ready pin
+ */
+static int ts7250_device_ready(struct mtd_info *mtd)
+{
+	return __raw_readb(TS72XX_NAND_BUSY_VIRT_BASE) & 0x20;
+}
+
+/*
+ * Main initialization routine
+ */
+static int __init ts7250_init(void)
+{
+	struct nand_chip *this;
+	const char *part_type = 0;
+	int mtd_parts_nb = 0;
+	struct mtd_partition *mtd_parts = 0;
+
+	if (!machine_is_ts72xx() || board_is_ts7200())
+		return -ENXIO;
+
+	/* Allocate memory for MTD device structure and private data */
+	ts7250_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
+	if (!ts7250_mtd) {
+		printk("Unable to allocate TS7250 NAND MTD device structure.\n");
+		return -ENOMEM;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *)(&ts7250_mtd[1]);
+
+	/* Initialize structures */
+	memset(ts7250_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	ts7250_mtd->priv = this;
+	ts7250_mtd->owner = THIS_MODULE;
+
+	/* insert callbacks */
+	this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE;
+	this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE;
+	this->cmd_ctrl = ts7250_hwcontrol;
+	this->dev_ready = ts7250_device_ready;
+	this->chip_delay = 15;
+	this->ecc.mode = NAND_ECC_SOFT;
+
+	printk("Searching for NAND flash...\n");
+	/* Scan to find existence of the device */
+	if (nand_scan(ts7250_mtd, 1)) {
+		kfree(ts7250_mtd);
+		return -ENXIO;
+	}
+#ifdef CONFIG_MTD_PARTITIONS
+	ts7250_mtd->name = "ts7250-nand";
+	mtd_parts_nb = parse_mtd_partitions(ts7250_mtd, part_probes, &mtd_parts, 0);
+	if (mtd_parts_nb > 0)
+		part_type = "command line";
+	else
+		mtd_parts_nb = 0;
+#endif
+	if (mtd_parts_nb == 0) {
+		mtd_parts = partition_info32;
+		if (ts7250_mtd->size >= (128 * 0x100000))
+			mtd_parts = partition_info128;
+		mtd_parts_nb = NUM_PARTITIONS;
+		part_type = "static";
+	}
+
+	/* Register the partitions */
+	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+	add_mtd_partitions(ts7250_mtd, mtd_parts, mtd_parts_nb);
+
+	/* Return happy */
+	return 0;
+}
+
+module_init(ts7250_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit ts7250_cleanup(void)
+{
+	/* Unregister the device */
+	del_mtd_device(ts7250_mtd);
+
+	/* Free the MTD device structure */
+	kfree(ts7250_mtd);
+}
+
+module_exit(ts7250_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jesse Off <joff@embeddedARM.com>");
+MODULE_DESCRIPTION("MTD map driver for Technologic Systems TS-7250 board");

+ 144 - 77
drivers/mtd/nftlcore.c

@@ -70,8 +70,6 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 	nftl->mbd.devnum = -1;
 	nftl->mbd.devnum = -1;
 	nftl->mbd.blksize = 512;
 	nftl->mbd.blksize = 512;
 	nftl->mbd.tr = tr;
 	nftl->mbd.tr = tr;
-	memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
-	nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
 
 
         if (NFTL_mount(nftl) < 0) {
         if (NFTL_mount(nftl) < 0) {
 		printk(KERN_WARNING "NFTL: could not mount device\n");
 		printk(KERN_WARNING "NFTL: could not mount device\n");
@@ -136,6 +134,69 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
 	kfree(nftl);
 	kfree(nftl);
 }
 }
 
 
+/*
+ * Read oob data from flash
+ */
+int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+		  size_t *retlen, uint8_t *buf)
+{
+	struct mtd_oob_ops ops;
+	int res;
+
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = offs & (mtd->writesize - 1);
+	ops.ooblen = len;
+	ops.oobbuf = buf;
+	ops.datbuf = NULL;
+	ops.len = len;
+
+	res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+	*retlen = ops.retlen;
+	return res;
+}
+
+/*
+ * Write oob data to flash
+ */
+int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+		   size_t *retlen, uint8_t *buf)
+{
+	struct mtd_oob_ops ops;
+	int res;
+
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = offs & (mtd->writesize - 1);
+	ops.ooblen = len;
+	ops.oobbuf = buf;
+	ops.datbuf = NULL;
+	ops.len = len;
+
+	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+	*retlen = ops.retlen;
+	return res;
+}
+
+/*
+ * Write data and oob to flash
+ */
+static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
+		      size_t *retlen, uint8_t *buf, uint8_t *oob)
+{
+	struct mtd_oob_ops ops;
+	int res;
+
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = offs;
+	ops.ooblen = mtd->oobsize;
+	ops.oobbuf = oob;
+	ops.datbuf = buf;
+	ops.len = len;
+
+	res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+	*retlen = ops.retlen;
+	return res;
+}
+
 #ifdef CONFIG_NFTL_RW
 #ifdef CONFIG_NFTL_RW
 
 
 /* Actual NFTL access routines */
 /* Actual NFTL access routines */
@@ -185,6 +246,7 @@ static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
 
 
 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
 {
 {
+	struct mtd_info *mtd = nftl->mbd.mtd;
 	u16 BlockMap[MAX_SECTORS_PER_UNIT];
 	u16 BlockMap[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
 	unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
@@ -194,7 +256,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
 	unsigned int targetEUN;
 	unsigned int targetEUN;
 	struct nftl_oob oob;
 	struct nftl_oob oob;
 	int inplace = 1;
 	int inplace = 1;
-        size_t retlen;
+	size_t retlen;
 
 
 	memset(BlockMap, 0xff, sizeof(BlockMap));
 	memset(BlockMap, 0xff, sizeof(BlockMap));
 	memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
 	memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
@@ -210,21 +272,21 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
 	/* Scan to find the Erase Unit which holds the actual data for each
 	/* Scan to find the Erase Unit which holds the actual data for each
 	   512-byte block within the Chain.
 	   512-byte block within the Chain.
 	*/
 	*/
-        silly = MAX_LOOPS;
+	silly = MAX_LOOPS;
 	targetEUN = BLOCK_NIL;
 	targetEUN = BLOCK_NIL;
 	while (thisEUN <= nftl->lastEUN ) {
 	while (thisEUN <= nftl->lastEUN ) {
-                unsigned int status, foldmark;
+		unsigned int status, foldmark;
 
 
 		targetEUN = thisEUN;
 		targetEUN = thisEUN;
 		for (block = 0; block < nftl->EraseSize / 512; block ++) {
 		for (block = 0; block < nftl->EraseSize / 512; block ++) {
-			MTD_READOOB(nftl->mbd.mtd,
-				    (thisEUN * nftl->EraseSize) + (block * 512),
-				    16 , &retlen, (char *)&oob);
+			nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
+				      (block * 512), 16 , &retlen,
+				      (char *)&oob);
 			if (block == 2) {
 			if (block == 2) {
-                                foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
-                                if (foldmark == FOLD_MARK_IN_PROGRESS) {
-                                        DEBUG(MTD_DEBUG_LEVEL1,
-                                              "Write Inhibited on EUN %d\n", thisEUN);
+				foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
+				if (foldmark == FOLD_MARK_IN_PROGRESS) {
+					DEBUG(MTD_DEBUG_LEVEL1,
+					      "Write Inhibited on EUN %d\n", thisEUN);
 					inplace = 0;
 					inplace = 0;
 				} else {
 				} else {
 					/* There's no other reason not to do inplace,
 					/* There's no other reason not to do inplace,
@@ -233,7 +295,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
 					inplace = 1;
 					inplace = 1;
 				}
 				}
 			}
 			}
-                        status = oob.b.Status | oob.b.Status1;
+			status = oob.b.Status | oob.b.Status1;
 			BlockLastState[block] = status;
 			BlockLastState[block] = status;
 
 
 			switch(status) {
 			switch(status) {
@@ -328,15 +390,15 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
 			return BLOCK_NIL;
 			return BLOCK_NIL;
 		}
 		}
 	} else {
 	} else {
-            /* We put a fold mark in the chain we are folding only if
-               we fold in place to help the mount check code. If we do
-               not fold in place, it is possible to find the valid
-               chain by selecting the longer one */
-            oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
-            oob.u.c.unused = 0xffffffff;
-            MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
-                         8, &retlen, (char *)&oob.u);
-        }
+		/* We put a fold mark in the chain we are folding only if we
+               fold in place to help the mount check code. If we do not fold in
+               place, it is possible to find the valid chain by selecting the
+               longer one */
+		oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
+		oob.u.c.unused = 0xffffffff;
+		nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
+			       8, &retlen, (char *)&oob.u);
+	}
 
 
 	/* OK. We now know the location of every block in the Virtual Unit Chain,
 	/* OK. We now know the location of every block in the Virtual Unit Chain,
 	   and the Erase Unit into which we are supposed to be copying.
 	   and the Erase Unit into which we are supposed to be copying.
@@ -353,33 +415,33 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
 			continue;
 			continue;
 		}
 		}
 
 
-                /* copy only in non free block (free blocks can only
+		/* copy only in non free block (free blocks can only
                    happen in case of media errors or deleted blocks) */
                    happen in case of media errors or deleted blocks) */
-                if (BlockMap[block] == BLOCK_NIL)
-                        continue;
-
-                ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
-				  512, &retlen, movebuf);
-                if (ret < 0) {
-                    ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
-                                      + (block * 512), 512, &retlen,
-                                      movebuf);
-                    if (ret != -EIO)
-                        printk("Error went away on retry.\n");
-                }
+		if (BlockMap[block] == BLOCK_NIL)
+			continue;
+
+		ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
+				512, &retlen, movebuf);
+		if (ret < 0 && ret != -EUCLEAN) {
+			ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
+					+ (block * 512), 512, &retlen,
+					movebuf);
+			if (ret != -EIO)
+				printk("Error went away on retry.\n");
+		}
 		memset(&oob, 0xff, sizeof(struct nftl_oob));
 		memset(&oob, 0xff, sizeof(struct nftl_oob));
 		oob.b.Status = oob.b.Status1 = SECTOR_USED;
 		oob.b.Status = oob.b.Status1 = SECTOR_USED;
-                MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
-                             512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo);
+
+		nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
+			   (block * 512), 512, &retlen, movebuf, (char *)&oob);
 	}
 	}
 
 
-        /* add the header so that it is now a valid chain */
-        oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
-                = cpu_to_le16(thisVUC);
-        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
+	/* add the header so that it is now a valid chain */
+	oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
+	oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
 
 
-        MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8,
-                     8, &retlen, (char *)&oob.u);
+	nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
+		       8, &retlen, (char *)&oob.u);
 
 
 	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
 	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
 
 
@@ -396,18 +458,18 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
 	while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
 	while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
 		unsigned int EUNtmp;
 		unsigned int EUNtmp;
 
 
-                EUNtmp = nftl->ReplUnitTable[thisEUN];
+		EUNtmp = nftl->ReplUnitTable[thisEUN];
 
 
-                if (NFTL_formatblock(nftl, thisEUN) < 0) {
+		if (NFTL_formatblock(nftl, thisEUN) < 0) {
 			/* could not erase : mark block as reserved
 			/* could not erase : mark block as reserved
 			 */
 			 */
 			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
 			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
-                } else {
+		} else {
 			/* correctly erased : mark it as free */
 			/* correctly erased : mark it as free */
 			nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
 			nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
 			nftl->numfreeEUNs++;
 			nftl->numfreeEUNs++;
-                }
-                thisEUN = EUNtmp;
+		}
+		thisEUN = EUNtmp;
 	}
 	}
 
 
 	/* Make this the new start of chain for thisVUC */
 	/* Make this the new start of chain for thisVUC */
@@ -473,6 +535,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
 {
 {
 	u16 lastEUN;
 	u16 lastEUN;
 	u16 thisVUC = block / (nftl->EraseSize / 512);
 	u16 thisVUC = block / (nftl->EraseSize / 512);
+	struct mtd_info *mtd = nftl->mbd.mtd;
 	unsigned int writeEUN;
 	unsigned int writeEUN;
 	unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
 	unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
 	size_t retlen;
 	size_t retlen;
@@ -489,21 +552,22 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
 		*/
 		*/
 		lastEUN = BLOCK_NIL;
 		lastEUN = BLOCK_NIL;
 		writeEUN = nftl->EUNtable[thisVUC];
 		writeEUN = nftl->EUNtable[thisVUC];
-                silly = MAX_LOOPS;
+		silly = MAX_LOOPS;
 		while (writeEUN <= nftl->lastEUN) {
 		while (writeEUN <= nftl->lastEUN) {
 			struct nftl_bci bci;
 			struct nftl_bci bci;
 			size_t retlen;
 			size_t retlen;
-                        unsigned int status;
+			unsigned int status;
 
 
 			lastEUN = writeEUN;
 			lastEUN = writeEUN;
 
 
-			MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
-				    8, &retlen, (char *)&bci);
+			nftl_read_oob(mtd,
+				      (writeEUN * nftl->EraseSize) + blockofs,
+				      8, &retlen, (char *)&bci);
 
 
 			DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
 			DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
 			      block , writeEUN, le16_to_cpu(bci.Status));
 			      block , writeEUN, le16_to_cpu(bci.Status));
 
 
-                        status = bci.Status | bci.Status1;
+			status = bci.Status | bci.Status1;
 			switch(status) {
 			switch(status) {
 			case SECTOR_FREE:
 			case SECTOR_FREE:
 				return writeEUN;
 				return writeEUN;
@@ -574,10 +638,10 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
 		/* We've found a free block. Insert it into the chain. */
 		/* We've found a free block. Insert it into the chain. */
 
 
 		if (lastEUN != BLOCK_NIL) {
 		if (lastEUN != BLOCK_NIL) {
-                    thisVUC |= 0x8000; /* It's a replacement block */
+			thisVUC |= 0x8000; /* It's a replacement block */
 		} else {
 		} else {
-                    /* The first block in a new chain */
-                    nftl->EUNtable[thisVUC] = writeEUN;
+			/* The first block in a new chain */
+			nftl->EUNtable[thisVUC] = writeEUN;
 		}
 		}
 
 
 		/* set up the actual EUN we're writing into */
 		/* set up the actual EUN we're writing into */
@@ -585,29 +649,29 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
 		nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
 		nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
 
 
 		/* ... and on the flash itself */
 		/* ... and on the flash itself */
-		MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
-			    &retlen, (char *)&oob.u);
+		nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
+			      &retlen, (char *)&oob.u);
 
 
 		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 
 
-		MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
-                             &retlen, (char *)&oob.u);
+		nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
+			       &retlen, (char *)&oob.u);
 
 
-                /* we link the new block to the chain only after the
+		/* we link the new block to the chain only after the
                    block is ready. It avoids the case where the chain
                    block is ready. It avoids the case where the chain
                    could point to a free block */
                    could point to a free block */
-                if (lastEUN != BLOCK_NIL) {
+		if (lastEUN != BLOCK_NIL) {
 			/* Both in our cache... */
 			/* Both in our cache... */
 			nftl->ReplUnitTable[lastEUN] = writeEUN;
 			nftl->ReplUnitTable[lastEUN] = writeEUN;
 			/* ... and on the flash itself */
 			/* ... and on the flash itself */
-			MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
-				    8, &retlen, (char *)&oob.u);
+			nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
+				      8, &retlen, (char *)&oob.u);
 
 
 			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
 			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
 				= cpu_to_le16(writeEUN);
 				= cpu_to_le16(writeEUN);
 
 
-			MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
-				     8, &retlen, (char *)&oob.u);
+			nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
+				       8, &retlen, (char *)&oob.u);
 		}
 		}
 
 
 		return writeEUN;
 		return writeEUN;
@@ -639,10 +703,9 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 
 
 	memset(&oob, 0xff, sizeof(struct nftl_oob));
 	memset(&oob, 0xff, sizeof(struct nftl_oob));
 	oob.b.Status = oob.b.Status1 = SECTOR_USED;
 	oob.b.Status = oob.b.Status1 = SECTOR_USED;
-	MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
-		     512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo);
-        /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */
 
 
+	nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
+		   512, &retlen, (char *)buffer, (char *)&oob);
 	return 0;
 	return 0;
 }
 }
 #endif /* CONFIG_NFTL_RW */
 #endif /* CONFIG_NFTL_RW */
@@ -651,20 +714,22 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 			  char *buffer)
 			  char *buffer)
 {
 {
 	struct NFTLrecord *nftl = (void *)mbd;
 	struct NFTLrecord *nftl = (void *)mbd;
+	struct mtd_info *mtd = nftl->mbd.mtd;
 	u16 lastgoodEUN;
 	u16 lastgoodEUN;
 	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
 	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
 	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
 	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
-        unsigned int status;
+	unsigned int status;
 	int silly = MAX_LOOPS;
 	int silly = MAX_LOOPS;
-        size_t retlen;
-        struct nftl_bci bci;
+	size_t retlen;
+	struct nftl_bci bci;
 
 
 	lastgoodEUN = BLOCK_NIL;
 	lastgoodEUN = BLOCK_NIL;
 
 
-        if (thisEUN != BLOCK_NIL) {
+	if (thisEUN != BLOCK_NIL) {
 		while (thisEUN < nftl->nb_blocks) {
 		while (thisEUN < nftl->nb_blocks) {
-			if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs,
-					8, &retlen, (char *)&bci) < 0)
+			if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
+					  blockofs, 8, &retlen,
+					  (char *)&bci) < 0)
 				status = SECTOR_IGNORE;
 				status = SECTOR_IGNORE;
 			else
 			else
 				status = bci.Status | bci.Status1;
 				status = bci.Status | bci.Status1;
@@ -694,7 +759,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 			}
 			}
 			thisEUN = nftl->ReplUnitTable[thisEUN];
 			thisEUN = nftl->ReplUnitTable[thisEUN];
 		}
 		}
-        }
+	}
 
 
  the_end:
  the_end:
 	if (lastgoodEUN == BLOCK_NIL) {
 	if (lastgoodEUN == BLOCK_NIL) {
@@ -703,7 +768,9 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
 	} else {
 	} else {
 		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
 		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
 		size_t retlen;
 		size_t retlen;
-		if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer))
+		int res = mtd->read(mtd, ptr, 512, &retlen, buffer);
+
+		if (res < 0 && res != -EUCLEAN)
 			return -EIO;
 			return -EIO;
 	}
 	}
 	return 0;
 	return 0;

+ 57 - 34
drivers/mtd/nftlmount.c

@@ -33,6 +33,11 @@
 
 
 char nftlmountrev[]="$Revision: 1.41 $";
 char nftlmountrev[]="$Revision: 1.41 $";
 
 
+extern int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+			 size_t *retlen, uint8_t *buf);
+extern int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+			  size_t *retlen, uint8_t *buf);
+
 /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
 /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
  *	various device information of the NFTL partition and Bad Unit Table. Update
  *	various device information of the NFTL partition and Bad Unit Table. Update
  *	the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
  *	the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
@@ -45,6 +50,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
 	size_t retlen;
 	size_t retlen;
 	u8 buf[SECTORSIZE];
 	u8 buf[SECTORSIZE];
 	struct NFTLMediaHeader *mh = &nftl->MediaHdr;
 	struct NFTLMediaHeader *mh = &nftl->MediaHdr;
+	struct mtd_info *mtd = nftl->mbd.mtd;
 	unsigned int i;
 	unsigned int i;
 
 
         /* Assume logical EraseSize == physical erasesize for starting the scan.
         /* Assume logical EraseSize == physical erasesize for starting the scan.
@@ -65,7 +71,8 @@ static int find_boot_record(struct NFTLrecord *nftl)
 
 
 		/* Check for ANAND header first. Then can whinge if it's found but later
 		/* Check for ANAND header first. Then can whinge if it's found but later
 		   checks fail */
 		   checks fail */
-		ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf);
+		ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
+				&retlen, buf);
 		/* We ignore ret in case the ECC of the MediaHeader is invalid
 		/* We ignore ret in case the ECC of the MediaHeader is invalid
 		   (which is apparently acceptable) */
 		   (which is apparently acceptable) */
 		if (retlen != SECTORSIZE) {
 		if (retlen != SECTORSIZE) {
@@ -90,8 +97,9 @@ static int find_boot_record(struct NFTLrecord *nftl)
 		}
 		}
 
 
 		/* To be safer with BIOS, also use erase mark as discriminant */
 		/* To be safer with BIOS, also use erase mark as discriminant */
-		if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
-				8, &retlen, (char *)&h1) < 0)) {
+		if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize +
+					 SECTORSIZE + 8, 8, &retlen,
+					 (char *)&h1) < 0)) {
 			printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
 			printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
 			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
 			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
 			continue;
 			continue;
@@ -109,8 +117,8 @@ static int find_boot_record(struct NFTLrecord *nftl)
 		}
 		}
 
 
 		/* Finally reread to check ECC */
 		/* Finally reread to check ECC */
-		if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE,
-				&retlen, buf, (char *)&oob, NULL) < 0)) {
+		if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
+				     &retlen, buf) < 0)) {
 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
 			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
 			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
 			continue;
 			continue;
@@ -228,9 +236,9 @@ device is already correct.
 The new DiskOnChip driver already scanned the bad block table.  Just query it.
 The new DiskOnChip driver already scanned the bad block table.  Just query it.
 			if ((i & (SECTORSIZE - 1)) == 0) {
 			if ((i & (SECTORSIZE - 1)) == 0) {
 				/* read one sector for every SECTORSIZE of blocks */
 				/* read one sector for every SECTORSIZE of blocks */
-				if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize +
-						       i + SECTORSIZE, SECTORSIZE, &retlen, buf,
-						       (char *)&oob, NULL)) < 0) {
+				if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize +
+						     i + SECTORSIZE, SECTORSIZE, &retlen,
+						     buf)) < 0) {
 					printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
 					printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
 					       ret);
 					       ret);
 					kfree(nftl->ReplUnitTable);
 					kfree(nftl->ReplUnitTable);
@@ -268,18 +276,22 @@ static int memcmpb(void *a, int c, int n)
 static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
 static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
 			      int check_oob)
 			      int check_oob)
 {
 {
-	int i;
-	size_t retlen;
 	u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize];
 	u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize];
+	struct mtd_info *mtd = nftl->mbd.mtd;
+	size_t retlen;
+	int i;
 
 
 	for (i = 0; i < len; i += SECTORSIZE) {
 	for (i = 0; i < len; i += SECTORSIZE) {
-		if (MTD_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0)
+		if (mtd->read(mtd, address, SECTORSIZE, &retlen, buf))
 			return -1;
 			return -1;
 		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
 		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
 			return -1;
 			return -1;
 
 
 		if (check_oob) {
 		if (check_oob) {
-			if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0)
+			if(nftl_read_oob(mtd, address, mtd->oobsize,
+					 &retlen, &buf[SECTORSIZE]) < 0)
+				return -1;
+			if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
 				return -1;
 				return -1;
 		}
 		}
 		address += SECTORSIZE;
 		address += SECTORSIZE;
@@ -301,10 +313,11 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
 	unsigned int nb_erases, erase_mark;
 	unsigned int nb_erases, erase_mark;
 	struct nftl_uci1 uci;
 	struct nftl_uci1 uci;
 	struct erase_info *instr = &nftl->instr;
 	struct erase_info *instr = &nftl->instr;
+	struct mtd_info *mtd = nftl->mbd.mtd;
 
 
 	/* Read the Unit Control Information #1 for Wear-Leveling */
 	/* Read the Unit Control Information #1 for Wear-Leveling */
-	if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
-			8, &retlen, (char *)&uci) < 0)
+	if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+			  8, &retlen, (char *)&uci) < 0)
 		goto default_uci1;
 		goto default_uci1;
 
 
 	erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
 	erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
@@ -321,7 +334,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
 	instr->mtd = nftl->mbd.mtd;
 	instr->mtd = nftl->mbd.mtd;
 	instr->addr = block * nftl->EraseSize;
 	instr->addr = block * nftl->EraseSize;
 	instr->len = nftl->EraseSize;
 	instr->len = nftl->EraseSize;
-	MTD_ERASE(nftl->mbd.mtd, instr);
+	mtd->erase(mtd, instr);
 
 
 	if (instr->state == MTD_ERASE_FAILED) {
 	if (instr->state == MTD_ERASE_FAILED) {
 		printk("Error while formatting block %d\n", block);
 		printk("Error while formatting block %d\n", block);
@@ -343,8 +356,8 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
 			goto fail;
 			goto fail;
 
 
 		uci.WearInfo = le32_to_cpu(nb_erases);
 		uci.WearInfo = le32_to_cpu(nb_erases);
-		if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
-				 &retlen, (char *)&uci) < 0)
+		if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
+				   8, 8, &retlen, (char *)&uci) < 0)
 			goto fail;
 			goto fail;
 		return 0;
 		return 0;
 fail:
 fail:
@@ -365,6 +378,7 @@ fail:
  *	case. */
  *	case. */
 static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
 static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
 {
 {
+	struct mtd_info *mtd = nftl->mbd.mtd;
 	unsigned int block, i, status;
 	unsigned int block, i, status;
 	struct nftl_bci bci;
 	struct nftl_bci bci;
 	int sectors_per_block;
 	int sectors_per_block;
@@ -374,8 +388,9 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b
 	block = first_block;
 	block = first_block;
 	for (;;) {
 	for (;;) {
 		for (i = 0; i < sectors_per_block; i++) {
 		for (i = 0; i < sectors_per_block; i++) {
-			if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE,
-					8, &retlen, (char *)&bci) < 0)
+			if (nftl_read_oob(mtd,
+					  block * nftl->EraseSize + i * SECTORSIZE,
+					  8, &retlen, (char *)&bci) < 0)
 				status = SECTOR_IGNORE;
 				status = SECTOR_IGNORE;
 			else
 			else
 				status = bci.Status | bci.Status1;
 				status = bci.Status | bci.Status1;
@@ -394,9 +409,10 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b
 					/* sector not free actually : mark it as SECTOR_IGNORE  */
 					/* sector not free actually : mark it as SECTOR_IGNORE  */
 					bci.Status = SECTOR_IGNORE;
 					bci.Status = SECTOR_IGNORE;
 					bci.Status1 = SECTOR_IGNORE;
 					bci.Status1 = SECTOR_IGNORE;
-					MTD_WRITEOOB(nftl->mbd.mtd,
-						     block * nftl->EraseSize + i * SECTORSIZE,
-						     8, &retlen, (char *)&bci);
+					nftl_write_oob(mtd, block *
+						       nftl->EraseSize +
+						       i * SECTORSIZE, 8,
+						       &retlen, (char *)&bci);
 				}
 				}
 				break;
 				break;
 			default:
 			default:
@@ -481,13 +497,14 @@ static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
  *	1. */
  *	1. */
 static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
 static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
 {
 {
+	struct mtd_info *mtd = nftl->mbd.mtd;
 	struct nftl_uci1 h1;
 	struct nftl_uci1 h1;
 	unsigned int erase_mark;
 	unsigned int erase_mark;
 	size_t retlen;
 	size_t retlen;
 
 
 	/* check erase mark. */
 	/* check erase mark. */
-	if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
-			&retlen, (char *)&h1) < 0)
+	if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+			  &retlen, (char *)&h1) < 0)
 		return -1;
 		return -1;
 
 
 	erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
 	erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
@@ -501,8 +518,9 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
 		h1.EraseMark = cpu_to_le16(ERASE_MARK);
 		h1.EraseMark = cpu_to_le16(ERASE_MARK);
 		h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
 		h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
 		h1.WearInfo = cpu_to_le32(0);
 		h1.WearInfo = cpu_to_le32(0);
-		if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
-				 &retlen, (char *)&h1) < 0)
+		if (nftl_write_oob(mtd,
+				   block * nftl->EraseSize + SECTORSIZE + 8, 8,
+				   &retlen, (char *)&h1) < 0)
 			return -1;
 			return -1;
 	} else {
 	} else {
 #if 0
 #if 0
@@ -513,8 +531,8 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
 						SECTORSIZE, 0) != 0)
 						SECTORSIZE, 0) != 0)
 				return -1;
 				return -1;
 
 
-			if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i,
-					16, &retlen, buf) < 0)
+			if (nftl_read_oob(mtd, block * nftl->EraseSize + i,
+					  16, &retlen, buf) < 0)
 				return -1;
 				return -1;
 			if (i == SECTORSIZE) {
 			if (i == SECTORSIZE) {
 				/* skip erase mark */
 				/* skip erase mark */
@@ -540,11 +558,12 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
  */
  */
 static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
 static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
 {
 {
+	struct mtd_info *mtd = nftl->mbd.mtd;
 	struct nftl_uci2 uci;
 	struct nftl_uci2 uci;
 	size_t retlen;
 	size_t retlen;
 
 
-	if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
-			8, &retlen, (char *)&uci) < 0)
+	if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
+			  8, &retlen, (char *)&uci) < 0)
 		return 0;
 		return 0;
 
 
 	return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
 	return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
@@ -558,6 +577,7 @@ int NFTL_mount(struct NFTLrecord *s)
 	int chain_length, do_format_chain;
 	int chain_length, do_format_chain;
 	struct nftl_uci0 h0;
 	struct nftl_uci0 h0;
 	struct nftl_uci1 h1;
 	struct nftl_uci1 h1;
+	struct mtd_info *mtd = s->mbd.mtd;
 	size_t retlen;
 	size_t retlen;
 
 
 	/* search for NFTL MediaHeader and Spare NFTL Media Header */
 	/* search for NFTL MediaHeader and Spare NFTL Media Header */
@@ -582,10 +602,13 @@ int NFTL_mount(struct NFTLrecord *s)
 
 
 			for (;;) {
 			for (;;) {
 				/* read the block header. If error, we format the chain */
 				/* read the block header. If error, we format the chain */
-				if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8,
-						&retlen, (char *)&h0) < 0 ||
-				    MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8,
-						&retlen, (char *)&h1) < 0) {
+				if (nftl_read_oob(mtd,
+						  block * s->EraseSize + 8, 8,
+						  &retlen, (char *)&h0) < 0 ||
+				    nftl_read_oob(mtd,
+						  block * s->EraseSize +
+						  SECTORSIZE + 8, 8,
+						  &retlen, (char *)&h1) < 0) {
 					s->ReplUnitTable[block] = BLOCK_NIL;
 					s->ReplUnitTable[block] = BLOCK_NIL;
 					do_format_chain = 1;
 					do_format_chain = 1;
 					break;
 					break;

+ 14 - 0
drivers/mtd/onenand/Kconfig

@@ -29,6 +29,20 @@ config MTD_ONENAND_GENERIC
 	help
 	help
 	  Support for OneNAND flash via platform device driver.
 	  Support for OneNAND flash via platform device driver.
 
 
+config MTD_ONENAND_OTP
+	bool "OneNAND OTP Support"
+	depends on MTD_ONENAND
+	help
+	  One Block of the NAND Flash Array memory is reserved as
+	  a One-Time Programmable Block memory area.
+	  Also, 1st Block of NAND Flash Array can be used as OTP.
+
+	  The OTP block can be read, programmed and locked using the same
+	  operations as any other NAND Flash Array memory block.
+	  OTP block cannot be erased.
+
+	  OTP block is fully-guaranteed to be a valid block.
+
 config MTD_ONENAND_SYNC_READ
 config MTD_ONENAND_SYNC_READ
 	bool "OneNAND Sync. Burst Read Support"
 	bool "OneNAND Sync. Burst Read Support"
 	depends on ARCH_OMAP
 	depends on ARCH_OMAP

+ 479 - 239
drivers/mtd/onenand/onenand_base.c

@@ -23,8 +23,7 @@
 /**
 /**
  * onenand_oob_64 - oob info for large (2KB) page
  * onenand_oob_64 - oob info for large (2KB) page
  */
  */
-static struct nand_oobinfo onenand_oob_64 = {
-	.useecc		= MTD_NANDECC_AUTOPLACE,
+static struct nand_ecclayout onenand_oob_64 = {
 	.eccbytes	= 20,
 	.eccbytes	= 20,
 	.eccpos		= {
 	.eccpos		= {
 		8, 9, 10, 11, 12,
 		8, 9, 10, 11, 12,
@@ -34,14 +33,14 @@ static struct nand_oobinfo onenand_oob_64 = {
 		},
 		},
 	.oobfree	= {
 	.oobfree	= {
 		{2, 3}, {14, 2}, {18, 3}, {30, 2},
 		{2, 3}, {14, 2}, {18, 3}, {30, 2},
-		{24, 3}, {46, 2}, {40, 3}, {62, 2} }
+		{34, 3}, {46, 2}, {50, 3}, {62, 2}
+	}
 };
 };
 
 
 /**
 /**
  * onenand_oob_32 - oob info for middle (1KB) page
  * onenand_oob_32 - oob info for middle (1KB) page
  */
  */
-static struct nand_oobinfo onenand_oob_32 = {
-	.useecc		= MTD_NANDECC_AUTOPLACE,
+static struct nand_ecclayout onenand_oob_32 = {
 	.eccbytes	= 10,
 	.eccbytes	= 10,
 	.eccpos		= {
 	.eccpos		= {
 		8, 9, 10, 11, 12,
 		8, 9, 10, 11, 12,
@@ -190,7 +189,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
 static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
 static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
 {
 {
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
-	int value, readcmd = 0;
+	int value, readcmd = 0, block_cmd = 0;
 	int block, page;
 	int block, page;
 	/* Now we use page size operation */
 	/* Now we use page size operation */
 	int sectors = 4, count = 4;
 	int sectors = 4, count = 4;
@@ -206,6 +205,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 
 
 	case ONENAND_CMD_ERASE:
 	case ONENAND_CMD_ERASE:
 	case ONENAND_CMD_BUFFERRAM:
 	case ONENAND_CMD_BUFFERRAM:
+	case ONENAND_CMD_OTP_ACCESS:
+		block_cmd = 1;
 		block = (int) (addr >> this->erase_shift);
 		block = (int) (addr >> this->erase_shift);
 		page = -1;
 		page = -1;
 		break;
 		break;
@@ -233,6 +234,12 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 		/* Write 'DFS, FBA' of Flash */
 		/* Write 'DFS, FBA' of Flash */
 		value = onenand_block_address(this, block);
 		value = onenand_block_address(this, block);
 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+
+		if (block_cmd) {
+			/* Select DataRAM for DDP */
+			value = onenand_bufferram_address(this, block);
+			this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+		}
 	}
 	}
 
 
 	if (page != -1) {
 	if (page != -1) {
@@ -301,6 +308,7 @@ 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);
@@ -344,7 +352,7 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
 
 
 	if (ONENAND_CURRENT_BUFFERRAM(this)) {
 	if (ONENAND_CURRENT_BUFFERRAM(this)) {
 		if (area == ONENAND_DATARAM)
 		if (area == ONENAND_DATARAM)
-			return mtd->oobblock;
+			return mtd->writesize;
 		if (area == ONENAND_SPARERAM)
 		if (area == ONENAND_SPARERAM)
 			return mtd->oobsize;
 			return mtd->oobsize;
 	}
 	}
@@ -372,6 +380,17 @@ static int onenand_read_bufferram(struct mtd_info *mtd, int area,
 
 
 	bufferram += onenand_bufferram_offset(mtd, area);
 	bufferram += onenand_bufferram_offset(mtd, area);
 
 
+	if (ONENAND_CHECK_BYTE_ACCESS(count)) {
+		unsigned short word;
+
+		/* Align with word(16-bit) size */
+		count--;
+
+		/* Read word and save byte */
+		word = this->read_word(bufferram + offset + count);
+		buffer[count] = (word & 0xff);
+	}
+
 	memcpy(buffer, bufferram + offset, count);
 	memcpy(buffer, bufferram + offset, count);
 
 
 	return 0;
 	return 0;
@@ -399,6 +418,17 @@ static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area,
 
 
 	this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
 	this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
 
 
+	if (ONENAND_CHECK_BYTE_ACCESS(count)) {
+		unsigned short word;
+
+		/* Align with word(16-bit) size */
+		count--;
+
+		/* Read word and save byte */
+		word = this->read_word(bufferram + offset + count);
+		buffer[count] = (word & 0xff);
+	}
+
 	memcpy(buffer, bufferram + offset, count);
 	memcpy(buffer, bufferram + offset, count);
 
 
 	this->mmcontrol(mtd, 0);
 	this->mmcontrol(mtd, 0);
@@ -426,6 +456,22 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
 
 
 	bufferram += onenand_bufferram_offset(mtd, area);
 	bufferram += onenand_bufferram_offset(mtd, area);
 
 
+	if (ONENAND_CHECK_BYTE_ACCESS(count)) {
+		unsigned short word;
+		int byte_offset;
+
+		/* Align with word(16-bit) size */
+		count--;
+
+		/* Calculate byte access offset */
+		byte_offset = offset + count;
+
+		/* Read word and save byte */
+		word = this->read_word(bufferram + byte_offset);
+		word = (word & ~0xff) | buffer[count];
+		this->write_word(word, bufferram + byte_offset);
+	}
+
 	memcpy(bufferram + offset, buffer, count);
 	memcpy(bufferram + offset, buffer, count);
 
 
 	return 0;
 	return 0;
@@ -549,31 +595,28 @@ static void onenand_release_device(struct mtd_info *mtd)
 }
 }
 
 
 /**
 /**
- * onenand_read_ecc - [MTD Interface] Read data with ECC
+ * onenand_read - [MTD Interface] Read data from flash
  * @param mtd		MTD device structure
  * @param mtd		MTD device structure
  * @param from		offset to read from
  * @param from		offset to read from
  * @param len		number of bytes to read
  * @param len		number of bytes to read
  * @param retlen	pointer to variable to store the number of read bytes
  * @param retlen	pointer to variable to store the number of read bytes
  * @param buf		the databuffer to put data
  * @param buf		the databuffer to put data
- * @param oob_buf	filesystem supplied oob data buffer
- * @param oobsel	oob selection structure
  *
  *
- * OneNAND read with ECC
- */
-static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
-	size_t *retlen, u_char *buf,
-	u_char *oob_buf, struct nand_oobinfo *oobsel)
+ * Read with ecc
+*/
+static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
+	size_t *retlen, u_char *buf)
 {
 {
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
 	int read = 0, column;
 	int read = 0, column;
 	int thislen;
 	int thislen;
 	int ret = 0;
 	int ret = 0;
 
 
-	DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ecc: 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);
 
 
 	/* Do not allow reads past end of device */
 	/* Do not allow reads past end of device */
 	if ((from + len) > mtd->size) {
 	if ((from + len) > mtd->size) {
-		DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: Attempt read beyond end of device\n");
+		DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n");
 		*retlen = 0;
 		*retlen = 0;
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -584,14 +627,14 @@ static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
 	/* TODO handling oob */
 	/* TODO handling oob */
 
 
 	while (read < len) {
 	while (read < len) {
-		thislen = min_t(int, mtd->oobblock, len - read);
+		thislen = min_t(int, mtd->writesize, len - read);
 
 
-		column = from & (mtd->oobblock - 1);
-		if (column + thislen > mtd->oobblock)
-			thislen = mtd->oobblock - column;
+		column = from & (mtd->writesize - 1);
+		if (column + thislen > mtd->writesize)
+			thislen = mtd->writesize - column;
 
 
 		if (!onenand_check_bufferram(mtd, from)) {
 		if (!onenand_check_bufferram(mtd, from)) {
-			this->command(mtd, ONENAND_CMD_READ, from, mtd->oobblock);
+			this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize);
 
 
 			ret = this->wait(mtd, FL_READING);
 			ret = this->wait(mtd, FL_READING);
 			/* First copy data and check return value for ECC handling */
 			/* First copy data and check return value for ECC handling */
@@ -606,7 +649,7 @@ static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
 			break;
 			break;
 
 
 		if (ret) {
 		if (ret) {
-			DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: read failed = %d\n", ret);
+			DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: read failed = %d\n", ret);
 			goto out;
 			goto out;
 		}
 		}
 
 
@@ -628,23 +671,7 @@ out:
 }
 }
 
 
 /**
 /**
- * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc
- * @param mtd		MTD device structure
- * @param from		offset to read from
- * @param len		number of bytes to read
- * @param retlen	pointer to variable to store the number of read bytes
- * @param buf		the databuffer to put data
- *
- * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL
-*/
-static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
-	size_t *retlen, u_char *buf)
-{
-	return onenand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL);
-}
-
-/**
- * onenand_read_oob - [MTD Interface] OneNAND read out-of-band
+ * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
  * @param mtd		MTD device structure
  * @param mtd		MTD device structure
  * @param from		offset to read from
  * @param from		offset to read from
  * @param len		number of bytes to read
  * @param len		number of bytes to read
@@ -653,8 +680,8 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
  *
  *
  * OneNAND read out-of-band data from the spare area
  * OneNAND read out-of-band data from the spare area
  */
  */
-static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-	size_t *retlen, u_char *buf)
+int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+			size_t *retlen, u_char *buf)
 {
 {
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
 	int read = 0, thislen, column;
 	int read = 0, thislen, column;
@@ -704,7 +731,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
 		/* Read more? */
 		/* Read more? */
 		if (read < len) {
 		if (read < len) {
 			/* Page size */
 			/* Page size */
-			from += mtd->oobblock;
+			from += mtd->writesize;
 			column = 0;
 			column = 0;
 		}
 		}
 	}
 	}
@@ -717,7 +744,52 @@ out:
 	return ret;
 	return ret;
 }
 }
 
 
+/**
+ * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @ops:	oob operation description structure
+ */
+static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
+			    struct mtd_oob_ops *ops)
+{
+	BUG_ON(ops->mode != MTD_OOB_PLACE);
+
+	return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->len,
+				   &ops->retlen, ops->oobbuf);
+}
+
 #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
 #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+/**
+ * onenand_verify_oob - [GENERIC] verify the oob contents after a write
+ * @param mtd		MTD device structure
+ * @param buf		the databuffer to verify
+ * @param to		offset to read from
+ * @param len		number of bytes to read and compare
+ *
+ */
+static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len)
+{
+	struct onenand_chip *this = mtd->priv;
+	char *readp = this->page_buf;
+	int column = to & (mtd->oobsize - 1);
+	int status, i;
+
+	this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
+	onenand_update_bufferram(mtd, to, 0);
+	status = this->wait(mtd, FL_READING);
+	if (status)
+		return status;
+
+	this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len);
+
+	for(i = 0; i < len; i++)
+		if (buf[i] != 0xFF && buf[i] != readp[i])
+			return -EBADMSG;
+
+	return 0;
+}
+
 /**
 /**
  * onenand_verify_page - [GENERIC] verify the chip contents after a write
  * onenand_verify_page - [GENERIC] verify the chip contents after a write
  * @param mtd		MTD device structure
  * @param mtd		MTD device structure
@@ -731,7 +803,7 @@ 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;
 
 
-	this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock);
+	this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
 
 
 	ret = this->wait(mtd, FL_READING);
 	ret = this->wait(mtd, FL_READING);
 	if (ret)
 	if (ret)
@@ -741,53 +813,51 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr)
 
 
 	/* Check, if the two dataram areas are same */
 	/* Check, if the two dataram areas are same */
 	dataram0 = this->base + ONENAND_DATARAM;
 	dataram0 = this->base + ONENAND_DATARAM;
-	dataram1 = dataram0 + mtd->oobblock;
+	dataram1 = dataram0 + mtd->writesize;
 
 
-	if (memcmp(dataram0, dataram1, mtd->oobblock))
+	if (memcmp(dataram0, dataram1, mtd->writesize))
 		return -EBADMSG;
 		return -EBADMSG;
 
 
 	return 0;
 	return 0;
 }
 }
 #else
 #else
 #define onenand_verify_page(...)	(0)
 #define onenand_verify_page(...)	(0)
+#define onenand_verify_oob(...)		(0)
 #endif
 #endif
 
 
-#define NOTALIGNED(x)	((x & (mtd->oobblock - 1)) != 0)
+#define NOTALIGNED(x)	((x & (mtd->writesize - 1)) != 0)
 
 
 /**
 /**
- * onenand_write_ecc - [MTD Interface] OneNAND write with ECC
+ * onenand_write - [MTD Interface] write buffer to FLASH
  * @param mtd		MTD device structure
  * @param mtd		MTD device structure
  * @param to		offset to write to
  * @param to		offset to write to
  * @param len		number of bytes to write
  * @param len		number of bytes to write
  * @param retlen	pointer to variable to store the number of written bytes
  * @param retlen	pointer to variable to store the number of written bytes
  * @param buf		the data to write
  * @param buf		the data to write
- * @param eccbuf	filesystem supplied oob data buffer
- * @param oobsel	oob selection structure
  *
  *
- * OneNAND write with ECC
+ * Write with ECC
  */
  */
-static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
-	size_t *retlen, const u_char *buf,
-	u_char *eccbuf, struct nand_oobinfo *oobsel)
+static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
+	size_t *retlen, const u_char *buf)
 {
 {
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
 	int written = 0;
 	int written = 0;
 	int ret = 0;
 	int ret = 0;
 
 
-	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ecc: 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);
 
 
 	/* Initialize retlen, in case of early exit */
 	/* Initialize retlen, in case of early exit */
 	*retlen = 0;
 	*retlen = 0;
 
 
 	/* Do not allow writes past end of device */
 	/* Do not allow writes past end of device */
 	if (unlikely((to + len) > mtd->size)) {
 	if (unlikely((to + len) > mtd->size)) {
-		DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt write to past end of device\n");
+		DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
 	/* Reject writes, which are not page aligned */
 	/* Reject writes, which are not page aligned */
         if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
         if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
-                DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt to write not page aligned data\n");
+                DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n");
                 return -EINVAL;
                 return -EINVAL;
         }
         }
 
 
@@ -796,20 +866,20 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 
 
 	/* Loop until all data write */
 	/* Loop until all data write */
 	while (written < len) {
 	while (written < len) {
-		int thislen = min_t(int, mtd->oobblock, len - written);
+		int thislen = min_t(int, mtd->writesize, len - written);
 
 
-		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock);
+		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->writesize);
 
 
 		this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen);
 		this->write_bufferram(mtd, ONENAND_DATARAM, buf, 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->oobblock);
+		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
 
 
 		onenand_update_bufferram(mtd, to, 1);
 		onenand_update_bufferram(mtd, to, 1);
 
 
 		ret = this->wait(mtd, FL_WRITING);
 		ret = this->wait(mtd, FL_WRITING);
 		if (ret) {
 		if (ret) {
-			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: write filaed %d\n", ret);
+			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret);
 			goto out;
 			goto out;
 		}
 		}
 
 
@@ -818,7 +888,7 @@ static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 		/* 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 *) buf, to);
 		if (ret) {
 		if (ret) {
-			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: verify failed %d\n", ret);
+			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret);
 			goto out;
 			goto out;
 		}
 		}
 
 
@@ -839,24 +909,7 @@ out:
 }
 }
 
 
 /**
 /**
- * onenand_write - [MTD Interface] compability function for onenand_write_ecc
- * @param mtd		MTD device structure
- * @param to		offset to write to
- * @param len		number of bytes to write
- * @param retlen	pointer to variable to store the number of written bytes
- * @param buf		the data to write
- *
- * This function simply calls onenand_write_ecc
- * with oob buffer and oobsel = NULL
- */
-static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
-	size_t *retlen, const u_char *buf)
-{
-	return onenand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL);
-}
-
-/**
- * onenand_write_oob - [MTD Interface] OneNAND write out-of-band
+ * onenand_do_write_oob - [Internal] OneNAND write out-of-band
  * @param mtd		MTD device structure
  * @param mtd		MTD device structure
  * @param to		offset to write to
  * @param to		offset to write to
  * @param len		number of bytes to write
  * @param len		number of bytes to write
@@ -865,11 +918,11 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
  *
  *
  * OneNAND write out-of-band
  * OneNAND write out-of-band
  */
  */
-static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
-	size_t *retlen, const u_char *buf)
+static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
+				size_t *retlen, const u_char *buf)
 {
 {
 	struct onenand_chip *this = mtd->priv;
 	struct onenand_chip *this = mtd->priv;
-	int column, status;
+	int column, ret = 0;
 	int written = 0;
 	int written = 0;
 
 
 	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
 	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -894,16 +947,27 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
 
 
 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
 
 
-		this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
-		this->write_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+		/* We send data to spare ram with oobsize
+		 * to prevent byte access */
+		memset(this->page_buf, 0xff, mtd->oobsize);
+		memcpy(this->page_buf + column, buf, thislen);
+		this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
 
 
 		this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
 		this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
 
 
 		onenand_update_bufferram(mtd, to, 0);
 		onenand_update_bufferram(mtd, to, 0);
 
 
-		status = this->wait(mtd, FL_WRITING);
-		if (status)
+		ret = this->wait(mtd, FL_WRITING);
+		if (ret) {
+			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret);
+			goto out;
+		}
+
+		ret = onenand_verify_oob(mtd, buf, to, thislen);
+		if (ret) {
+			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
 			goto out;
 			goto out;
+		}
 
 
 		written += thislen;
 		written += thislen;
 
 
@@ -920,145 +984,22 @@ out:
 
 
 	*retlen = written;
 	*retlen = written;
 
 
-	return 0;
+	return ret;
 }
 }
 
 
 /**
 /**
- * onenand_writev_ecc - [MTD Interface] write with iovec with ecc
- * @param mtd		MTD device structure
- * @param vecs		the iovectors to write
- * @param count		number of vectors
- * @param to		offset to write to
- * @param retlen	pointer to variable to store the number of written bytes
- * @param eccbuf	filesystem supplied oob data buffer
- * @param oobsel	oob selection structure
- *
- * OneNAND write with iovec with ecc
+ * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @ops:	oob operation description structure
  */
  */
-static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
-	unsigned long count, loff_t to, size_t *retlen,
-	u_char *eccbuf, struct nand_oobinfo *oobsel)
+static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops)
 {
 {
-	struct onenand_chip *this = mtd->priv;
-	unsigned char *pbuf;
-	size_t total_len, len;
-	int i, written = 0;
-	int ret = 0;
-
-	/* Preset written len for early exit */
-	*retlen = 0;
-
-	/* Calculate total length of data */
-	total_len = 0;
-	for (i = 0; i < count; i++)
-		total_len += vecs[i].iov_len;
-
-	DEBUG(MTD_DEBUG_LEVEL3, "onenand_writev_ecc: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
-
-	/* Do not allow write past end of the device */
-	if (unlikely((to + total_len) > mtd->size)) {
-		DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempted write past end of device\n");
-		return -EINVAL;
-	}
-
-	/* Reject writes, which are not page aligned */
-        if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(total_len))) {
-                DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempt to write not page aligned data\n");
-                return -EINVAL;
-        }
-
-	/* Grab the lock and see if the device is available */
-	onenand_get_device(mtd, FL_WRITING);
-
-	/* TODO handling oob */
-
-	/* Loop until all keve's data has been written */
-	len = 0;
-	while (count) {
-		pbuf = this->page_buf;
-		/*
-		 * If the given tuple is >= pagesize then
-		 * write it out from the iov
-		 */
-		if ((vecs->iov_len - len) >= mtd->oobblock) {
-			pbuf = vecs->iov_base + len;
-
-			len += mtd->oobblock;
-
-			/* Check, if we have to switch to the next tuple */
-			if (len >= (int) vecs->iov_len) {
-				vecs++;
-				len = 0;
-				count--;
-			}
-		} else {
-			int cnt = 0, thislen;
-			while (cnt < mtd->oobblock) {
-				thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len);
-				memcpy(this->page_buf + cnt, vecs->iov_base + len, thislen);
-				cnt += thislen;
-				len += thislen;
-
-				/* Check, if we have to switch to the next tuple */
-				if (len >= (int) vecs->iov_len) {
-					vecs++;
-					len = 0;
-					count--;
-				}
-			}
-		}
-
-		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock);
+	BUG_ON(ops->mode != MTD_OOB_PLACE);
 
 
-		this->write_bufferram(mtd, ONENAND_DATARAM, pbuf, 0, mtd->oobblock);
-		this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
-
-		this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock);
-
-		onenand_update_bufferram(mtd, to, 1);
-
-		ret = this->wait(mtd, FL_WRITING);
-		if (ret) {
-			DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: write failed %d\n", ret);
-			goto out;
-		}
-
-
-		/* Only check verify write turn on */
-		ret = onenand_verify_page(mtd, (u_char *) pbuf, to);
-		if (ret) {
-			DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: verify failed %d\n", ret);
-			goto out;
-		}
-
-		written += mtd->oobblock;
-
-		to += mtd->oobblock;
-	}
-
-out:
-	/* Deselect and wakt up anyone waiting on the device */
-	onenand_release_device(mtd);
-
-	*retlen = written;
-
-	return 0;
-}
-
-/**
- * onenand_writev - [MTD Interface] compabilty function for onenand_writev_ecc
- * @param mtd		MTD device structure
- * @param vecs		the iovectors to write
- * @param count		number of vectors
- * @param to		offset to write to
- * @param retlen	pointer to variable to store the number of written bytes
- *
- * OneNAND write with kvec. This just calls the ecc function
- */
-static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs,
-	unsigned long count, loff_t to, size_t *retlen)
-{
-	return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL);
+	return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->len,
+				    &ops->retlen, ops->oobbuf);
 }
 }
 
 
 /**
 /**
@@ -1227,7 +1168,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
 
 
         /* We write two bytes, so we dont have to mess with 16 bit access */
         /* We write two bytes, so we dont have to mess with 16 bit access */
         ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
         ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
-        return mtd->write_oob(mtd, ofs , 2, &retlen, buf);
+        return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
 }
 }
 
 
 /**
 /**
@@ -1324,6 +1265,304 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_MTD_ONENAND_OTP
+
+/* Interal OTP operation */
+typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
+		size_t *retlen, u_char *buf);
+
+/**
+ * do_otp_read - [DEFAULT] Read OTP block area
+ * @param mtd		MTD device structure
+ * @param from		The offset to read
+ * @param len		number of bytes to read
+ * @param retlen	pointer to variable to store the number of readbytes
+ * @param buf		the databuffer to put/get data
+ *
+ * Read OTP block area.
+ */
+static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char *buf)
+{
+	struct onenand_chip *this = mtd->priv;
+	int ret;
+
+	/* Enter OTP access mode */
+	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+	this->wait(mtd, FL_OTPING);
+
+	ret = mtd->read(mtd, from, len, retlen, buf);
+
+	/* Exit OTP access mode */
+	this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+	this->wait(mtd, FL_RESETING);
+
+	return ret;
+}
+
+/**
+ * do_otp_write - [DEFAULT] Write OTP block area
+ * @param mtd		MTD device structure
+ * @param from		The offset to write
+ * @param len		number of bytes to write
+ * @param retlen	pointer to variable to store the number of write bytes
+ * @param buf		the databuffer to put/get data
+ *
+ * Write OTP block area.
+ */
+static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char *buf)
+{
+	struct onenand_chip *this = mtd->priv;
+	unsigned char *pbuf = buf;
+	int ret;
+
+	/* Force buffer page aligned */
+	if (len < mtd->writesize) {
+		memcpy(this->page_buf, buf, len);
+		memset(this->page_buf + len, 0xff, mtd->writesize - len);
+		pbuf = this->page_buf;
+		len = mtd->writesize;
+	}
+
+	/* Enter OTP access mode */
+	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+	this->wait(mtd, FL_OTPING);
+
+	ret = mtd->write(mtd, from, len, retlen, pbuf);
+
+	/* Exit OTP access mode */
+	this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+	this->wait(mtd, FL_RESETING);
+
+	return ret;
+}
+
+/**
+ * do_otp_lock - [DEFAULT] Lock OTP block area
+ * @param mtd		MTD device structure
+ * @param from		The offset to lock
+ * @param len		number of bytes to lock
+ * @param retlen	pointer to variable to store the number of lock bytes
+ * @param buf		the databuffer to put/get data
+ *
+ * Lock OTP block area.
+ */
+static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char *buf)
+{
+	struct onenand_chip *this = mtd->priv;
+	int ret;
+
+	/* Enter OTP access mode */
+	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+	this->wait(mtd, FL_OTPING);
+
+	ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+
+	/* Exit OTP access mode */
+	this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+	this->wait(mtd, FL_RESETING);
+
+	return ret;
+}
+
+/**
+ * onenand_otp_walk - [DEFAULT] Handle OTP operation
+ * @param mtd		MTD device structure
+ * @param from		The offset to read/write
+ * @param len		number of bytes to read/write
+ * @param retlen	pointer to variable to store the number of read bytes
+ * @param buf		the databuffer to put/get data
+ * @param action	do given action
+ * @param mode		specify user and factory
+ *
+ * Handle OTP operation.
+ */
+static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
+			size_t *retlen, u_char *buf,
+			otp_op_t action, int mode)
+{
+	struct onenand_chip *this = mtd->priv;
+	int otp_pages;
+	int density;
+	int ret = 0;
+
+	*retlen = 0;
+
+	density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+	if (density < ONENAND_DEVICE_DENSITY_512Mb)
+		otp_pages = 20;
+	else
+		otp_pages = 10;
+
+	if (mode == MTD_OTP_FACTORY) {
+		from += mtd->writesize * otp_pages;
+		otp_pages = 64 - otp_pages;
+	}
+
+	/* Check User/Factory boundary */
+	if (((mtd->writesize * otp_pages) - (from + len)) < 0)
+		return 0;
+
+	while (len > 0 && otp_pages > 0) {
+		if (!action) {	/* OTP Info functions */
+			struct otp_info *otpinfo;
+
+			len -= sizeof(struct otp_info);
+			if (len <= 0)
+				return -ENOSPC;
+
+			otpinfo = (struct otp_info *) buf;
+			otpinfo->start = from;
+			otpinfo->length = mtd->writesize;
+			otpinfo->locked = 0;
+
+			from += mtd->writesize;
+			buf += sizeof(struct otp_info);
+			*retlen += sizeof(struct otp_info);
+		} else {
+			size_t tmp_retlen;
+			int size = len;
+
+			ret = action(mtd, from, len, &tmp_retlen, buf);
+
+			buf += size;
+			len -= size;
+			*retlen += size;
+
+			if (ret < 0)
+				return ret;
+		}
+		otp_pages--;
+	}
+
+	return 0;
+}
+
+/**
+ * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
+ * @param mtd		MTD device structure
+ * @param buf		the databuffer to put/get data
+ * @param len		number of bytes to read
+ *
+ * Read factory OTP info.
+ */
+static int onenand_get_fact_prot_info(struct mtd_info *mtd,
+			struct otp_info *buf, size_t len)
+{
+	size_t retlen;
+	int ret;
+
+	ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY);
+
+	return ret ? : retlen;
+}
+
+/**
+ * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area
+ * @param mtd		MTD device structure
+ * @param from		The offset to read
+ * @param len		number of bytes to read
+ * @param retlen	pointer to variable to store the number of read bytes
+ * @param buf		the databuffer to put/get data
+ *
+ * Read factory OTP area.
+ */
+static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
+			size_t len, size_t *retlen, u_char *buf)
+{
+	return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY);
+}
+
+/**
+ * onenand_get_user_prot_info - [MTD Interface] Read user OTP info
+ * @param mtd		MTD device structure
+ * @param buf		the databuffer to put/get data
+ * @param len		number of bytes to read
+ *
+ * Read user OTP info.
+ */
+static int onenand_get_user_prot_info(struct mtd_info *mtd,
+			struct otp_info *buf, size_t len)
+{
+	size_t retlen;
+	int ret;
+
+	ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER);
+
+	return ret ? : retlen;
+}
+
+/**
+ * onenand_read_user_prot_reg - [MTD Interface] Read user OTP area
+ * @param mtd		MTD device structure
+ * @param from		The offset to read
+ * @param len		number of bytes to read
+ * @param retlen	pointer to variable to store the number of read bytes
+ * @param buf		the databuffer to put/get data
+ *
+ * Read user OTP area.
+ */
+static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
+			size_t len, size_t *retlen, u_char *buf)
+{
+	return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER);
+}
+
+/**
+ * onenand_write_user_prot_reg - [MTD Interface] Write user OTP area
+ * @param mtd		MTD device structure
+ * @param from		The offset to write
+ * @param len		number of bytes to write
+ * @param retlen	pointer to variable to store the number of write bytes
+ * @param buf		the databuffer to put/get data
+ *
+ * Write user OTP area.
+ */
+static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+			size_t len, size_t *retlen, u_char *buf)
+{
+	return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER);
+}
+
+/**
+ * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area
+ * @param mtd		MTD device structure
+ * @param from		The offset to lock
+ * @param len		number of bytes to unlock
+ *
+ * Write lock mark on spare area in page 0 in OTP block
+ */
+static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
+			size_t len)
+{
+	unsigned char oob_buf[64];
+	size_t retlen;
+	int ret;
+
+	memset(oob_buf, 0xff, mtd->oobsize);
+	/*
+	 * Note: OTP lock operation
+	 *       OTP block : 0xXXFC
+	 *       1st block : 0xXXF3 (If chip support)
+	 *       Both      : 0xXXF0 (If chip support)
+	 */
+	oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
+
+	/*
+	 * Write lock mark to 8th word of sector0 of page0 of the spare0.
+	 * We write 16 bytes spare area instead of 2 bytes.
+	 */
+	from = 0;
+	len = 16;
+
+	ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER);
+
+	return ret ? : retlen;
+}
+#endif	/* CONFIG_MTD_ONENAND_OTP */
+
 /**
 /**
  * onenand_print_device_info - Print device ID
  * onenand_print_device_info - Print device ID
  * @param device        device ID
  * @param device        device ID
@@ -1423,15 +1662,15 @@ static int onenand_probe(struct mtd_info *mtd)
 
 
 	/* OneNAND page size & block size */
 	/* OneNAND page size & block size */
 	/* The data buffer size is equal to page size */
 	/* The data buffer size is equal to page size */
-	mtd->oobblock = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
-	mtd->oobsize = mtd->oobblock >> 5;
+	mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
+	mtd->oobsize = mtd->writesize >> 5;
 	/* Pagers per block is always 64 in OneNAND */
 	/* Pagers per block is always 64 in OneNAND */
-	mtd->erasesize = mtd->oobblock << 6;
+	mtd->erasesize = mtd->writesize << 6;
 
 
 	this->erase_shift = ffs(mtd->erasesize) - 1;
 	this->erase_shift = ffs(mtd->erasesize) - 1;
-	this->page_shift = ffs(mtd->oobblock) - 1;
+	this->page_shift = ffs(mtd->writesize) - 1;
 	this->ppb_shift = (this->erase_shift - this->page_shift);
 	this->ppb_shift = (this->erase_shift - this->page_shift);
-	this->page_mask = (mtd->erasesize / mtd->oobblock) - 1;
+	this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
 
 
 	/* REVIST: Multichip handling */
 	/* REVIST: Multichip handling */
 
 
@@ -1475,7 +1714,6 @@ static void onenand_resume(struct mtd_info *mtd)
 				"in suspended state\n");
 				"in suspended state\n");
 }
 }
 
 
-
 /**
 /**
  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
  * @param mtd		MTD device structure
  * @param mtd		MTD device structure
@@ -1522,7 +1760,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 	/* Allocate buffers, if necessary */
 	/* Allocate buffers, if necessary */
 	if (!this->page_buf) {
 	if (!this->page_buf) {
 		size_t len;
 		size_t len;
-		len = mtd->oobblock + mtd->oobsize;
+		len = mtd->writesize + mtd->oobsize;
 		this->page_buf = kmalloc(len, GFP_KERNEL);
 		this->page_buf = kmalloc(len, GFP_KERNEL);
 		if (!this->page_buf) {
 		if (!this->page_buf) {
 			printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
 			printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
@@ -1537,40 +1775,42 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 
 
 	switch (mtd->oobsize) {
 	switch (mtd->oobsize) {
 	case 64:
 	case 64:
-		this->autooob = &onenand_oob_64;
+		this->ecclayout = &onenand_oob_64;
 		break;
 		break;
 
 
 	case 32:
 	case 32:
-		this->autooob = &onenand_oob_32;
+		this->ecclayout = &onenand_oob_32;
 		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);
 		/* To prevent kernel oops */
 		/* To prevent kernel oops */
-		this->autooob = &onenand_oob_32;
+		this->ecclayout = &onenand_oob_32;
 		break;
 		break;
 	}
 	}
 
 
-	memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
+	mtd->ecclayout = this->ecclayout;
 
 
 	/* Fill in remaining MTD driver data */
 	/* Fill in remaining MTD driver data */
 	mtd->type = MTD_NANDFLASH;
 	mtd->type = MTD_NANDFLASH;
-	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+	mtd->flags = MTD_CAP_NANDFLASH;
 	mtd->ecctype = MTD_ECC_SW;
 	mtd->ecctype = MTD_ECC_SW;
 	mtd->erase = onenand_erase;
 	mtd->erase = onenand_erase;
 	mtd->point = NULL;
 	mtd->point = NULL;
 	mtd->unpoint = NULL;
 	mtd->unpoint = NULL;
 	mtd->read = onenand_read;
 	mtd->read = onenand_read;
 	mtd->write = onenand_write;
 	mtd->write = onenand_write;
-	mtd->read_ecc = onenand_read_ecc;
-	mtd->write_ecc = onenand_write_ecc;
 	mtd->read_oob = onenand_read_oob;
 	mtd->read_oob = onenand_read_oob;
 	mtd->write_oob = onenand_write_oob;
 	mtd->write_oob = onenand_write_oob;
-	mtd->readv = NULL;
-	mtd->readv_ecc = NULL;
-	mtd->writev = onenand_writev;
-	mtd->writev_ecc = onenand_writev_ecc;
+#ifdef CONFIG_MTD_ONENAND_OTP
+	mtd->get_fact_prot_info = onenand_get_fact_prot_info;
+	mtd->read_fact_prot_reg = onenand_read_fact_prot_reg;
+	mtd->get_user_prot_info = onenand_get_user_prot_info;
+	mtd->read_user_prot_reg = onenand_read_user_prot_reg;
+	mtd->write_user_prot_reg = onenand_write_user_prot_reg;
+	mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
+#endif
 	mtd->sync = onenand_sync;
 	mtd->sync = onenand_sync;
 	mtd->lock = NULL;
 	mtd->lock = NULL;
 	mtd->unlock = onenand_unlock;
 	mtd->unlock = onenand_unlock;

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

@@ -17,6 +17,9 @@
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/compatmac.h>
 
 
+extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+			       size_t *retlen, u_char *buf);
+
 /**
 /**
  * check_short_pattern - [GENERIC] check if a pattern is in the buffer
  * check_short_pattern - [GENERIC] check if a pattern is in the buffer
  * @param buf		the buffer to search
  * @param buf		the buffer to search
@@ -87,13 +90,13 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 
 
 			/* No need to read pages fully,
 			/* No need to read pages fully,
 			 * just read required OOB bytes */
 			 * just read required OOB bytes */
-			ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs,
-						readlen, &retlen, &buf[0]);
+			ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
+						  readlen, &retlen, &buf[0]);
 
 
 			if (ret)
 			if (ret)
 				return ret;
 				return ret;
 
 
-			if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->oobblock, 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);

+ 9 - 9
drivers/mtd/redboot.c

@@ -1,5 +1,5 @@
 /*
 /*
- * $Id: redboot.c,v 1.19 2005/12/01 10:03:51 dwmw2 Exp $
+ * $Id: redboot.c,v 1.21 2006/03/30 18:34:37 bjd Exp $
  *
  *
  * Parse RedBoot-style Flash Image System (FIS) tables and
  * Parse RedBoot-style Flash Image System (FIS) tables and
  * produce a Linux partition array to match.
  * produce a Linux partition array to match.
@@ -15,14 +15,14 @@
 
 
 struct fis_image_desc {
 struct fis_image_desc {
     unsigned char name[16];      // Null terminated name
     unsigned char name[16];      // Null terminated name
-    unsigned long flash_base;    // Address within FLASH of image
-    unsigned long mem_base;      // Address in memory where it executes
-    unsigned long size;          // Length of image
-    unsigned long entry_point;   // Execution entry point
-    unsigned long data_length;   // Length of actual data
-    unsigned char _pad[256-(16+7*sizeof(unsigned long))];
-    unsigned long desc_cksum;    // Checksum over image descriptor
-    unsigned long file_cksum;    // Checksum over image data
+    uint32_t	  flash_base;    // Address within FLASH of image
+    uint32_t	  mem_base;      // Address in memory where it executes
+    uint32_t	  size;          // Length of image
+    uint32_t	  entry_point;   // Execution entry point
+    uint32_t	  data_length;   // Length of actual data
+    unsigned char _pad[256-(16+7*sizeof(uint32_t))];
+    uint32_t	  desc_cksum;    // Checksum over image descriptor
+    uint32_t	  file_cksum;    // Checksum over image data
 };
 };
 
 
 struct fis_list {
 struct fis_list {

+ 25 - 23
drivers/mtd/rfd_ftl.c

@@ -3,7 +3,7 @@
  *
  *
  * Copyright (C) 2005  Sean Young <sean@mess.org>
  * Copyright (C) 2005  Sean Young <sean@mess.org>
  *
  *
- * $Id: rfd_ftl.c,v 1.5 2005/11/07 11:14:21 gleixner Exp $
+ * $Id: rfd_ftl.c,v 1.8 2006/01/15 12:51:44 sean Exp $
  *
  *
  * This type of flash translation layer (FTL) is used by the Embedded BIOS
  * This type of flash translation layer (FTL) is used by the Embedded BIOS
  * by General Software. It is known as the Resident Flash Disk (RFD), see:
  * by General Software. It is known as the Resident Flash Disk (RFD), see:
@@ -61,6 +61,7 @@ struct block {
 		BLOCK_OK,
 		BLOCK_OK,
 		BLOCK_ERASING,
 		BLOCK_ERASING,
 		BLOCK_ERASED,
 		BLOCK_ERASED,
+		BLOCK_UNUSED,
 		BLOCK_FAILED
 		BLOCK_FAILED
 	} state;
 	} state;
 	int free_sectors;
 	int free_sectors;
@@ -99,10 +100,8 @@ static int build_block_map(struct partition *part, int block_no)
 	block->offset = part->block_size * block_no;
 	block->offset = part->block_size * block_no;
 
 
 	if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
 	if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
-		block->state = BLOCK_ERASED; /* assumption */
-		block->free_sectors = part->data_sectors_per_block;
-		part->reserved_block = block_no;
-		return 1;
+		block->state = BLOCK_UNUSED;
+		return -ENOENT;
 	}
 	}
 
 
 	block->state = BLOCK_OK;
 	block->state = BLOCK_OK;
@@ -124,7 +123,7 @@ static int build_block_map(struct partition *part, int block_no)
 			entry = 0;
 			entry = 0;
 
 
 		if (entry >= part->sector_count) {
 		if (entry >= part->sector_count) {
-			printk(KERN_NOTICE PREFIX
+			printk(KERN_WARNING PREFIX
 				"'%s': unit #%d: entry %d corrupt, "
 				"'%s': unit #%d: entry %d corrupt, "
 				"sector %d out of range\n",
 				"sector %d out of range\n",
 				part->mbd.mtd->name, block_no, i, entry);
 				part->mbd.mtd->name, block_no, i, entry);
@@ -132,7 +131,7 @@ static int build_block_map(struct partition *part, int block_no)
 		}
 		}
 
 
 		if (part->sector_map[entry] != -1) {
 		if (part->sector_map[entry] != -1) {
-			printk(KERN_NOTICE PREFIX
+			printk(KERN_WARNING PREFIX
 				"'%s': more than one entry for sector %d\n",
 				"'%s': more than one entry for sector %d\n",
 				part->mbd.mtd->name, entry);
 				part->mbd.mtd->name, entry);
 			part->errors = 1;
 			part->errors = 1;
@@ -167,7 +166,7 @@ static int scan_header(struct partition *part)
 	/* each erase block has three bytes header, followed by the map */
 	/* each erase block has three bytes header, followed by the map */
 	part->header_sectors_per_block =
 	part->header_sectors_per_block =
 			((HEADER_MAP_OFFSET + sectors_per_block) *
 			((HEADER_MAP_OFFSET + sectors_per_block) *
-		 	sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
+			sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
 
 
 	part->data_sectors_per_block = sectors_per_block -
 	part->data_sectors_per_block = sectors_per_block -
 			part->header_sectors_per_block;
 			part->header_sectors_per_block;
@@ -226,7 +225,7 @@ static int scan_header(struct partition *part)
 	}
 	}
 
 
 	if (part->reserved_block == -1) {
 	if (part->reserved_block == -1) {
-		printk(KERN_NOTICE PREFIX "'%s': no empty erase unit found\n",
+		printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
 				part->mbd.mtd->name);
 				part->mbd.mtd->name);
 
 
 		part->errors = 1;
 		part->errors = 1;
@@ -315,7 +314,7 @@ static void erase_callback(struct erase_info *erase)
 		rc = -EIO;
 		rc = -EIO;
 
 
 	if (rc) {
 	if (rc) {
-		printk(KERN_NOTICE PREFIX "'%s': unable to write RFD "
+		printk(KERN_ERR PREFIX "'%s': unable to write RFD "
 				"header at 0x%lx\n",
 				"header at 0x%lx\n",
 				part->mbd.mtd->name,
 				part->mbd.mtd->name,
 				part->blocks[i].offset);
 				part->blocks[i].offset);
@@ -348,7 +347,7 @@ static int erase_block(struct partition *part, int block)
 	rc = part->mbd.mtd->erase(part->mbd.mtd, erase);
 	rc = part->mbd.mtd->erase(part->mbd.mtd, erase);
 
 
 	if (rc) {
 	if (rc) {
-		printk(KERN_WARNING PREFIX "erase of region %x,%x on '%s' "
+		printk(KERN_ERR PREFIX "erase of region %x,%x on '%s' "
 				"failed\n", erase->addr, erase->len,
 				"failed\n", erase->addr, erase->len,
 				part->mbd.mtd->name);
 				part->mbd.mtd->name);
 		kfree(erase);
 		kfree(erase);
@@ -383,7 +382,7 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old
 		rc = -EIO;
 		rc = -EIO;
 
 
 	if (rc) {
 	if (rc) {
-		printk(KERN_NOTICE PREFIX "error reading '%s' at "
+		printk(KERN_ERR PREFIX "error reading '%s' at "
 			"0x%lx\n", part->mbd.mtd->name,
 			"0x%lx\n", part->mbd.mtd->name,
 			part->blocks[block_no].offset);
 			part->blocks[block_no].offset);
 
 
@@ -423,7 +422,7 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old
 			rc = -EIO;
 			rc = -EIO;
 
 
 		if (rc) {
 		if (rc) {
-			printk(KERN_NOTICE PREFIX "'%s': Unable to "
+			printk(KERN_ERR PREFIX "'%s': Unable to "
 				"read sector for relocation\n",
 				"read sector for relocation\n",
 				part->mbd.mtd->name);
 				part->mbd.mtd->name);
 
 
@@ -520,7 +519,7 @@ static int reclaim_block(struct partition *part, u_long *old_sector)
  * because if we fill that one up first it'll have the most chance of having
  * because if we fill that one up first it'll have the most chance of having
  * the least live sectors at reclaim.
  * the least live sectors at reclaim.
  */
  */
-static int find_free_block(const struct partition *part)
+static int find_free_block(struct partition *part)
 {
 {
 	int block, stop;
 	int block, stop;
 
 
@@ -533,6 +532,9 @@ static int find_free_block(const struct partition *part)
 				block != part->reserved_block)
 				block != part->reserved_block)
 			return block;
 			return block;
 
 
+		if (part->blocks[block].state == BLOCK_UNUSED)
+			erase_block(part, block);
+
 		if (++block >= part->total_blocks)
 		if (++block >= part->total_blocks)
 			block = 0;
 			block = 0;
 
 
@@ -541,7 +543,7 @@ static int find_free_block(const struct partition *part)
 	return -1;
 	return -1;
 }
 }
 
 
-static int find_writeable_block(struct partition *part, u_long *old_sector)
+static int find_writable_block(struct partition *part, u_long *old_sector)
 {
 {
 	int rc, block;
 	int rc, block;
 	size_t retlen;
 	size_t retlen;
@@ -570,7 +572,7 @@ static int find_writeable_block(struct partition *part, u_long *old_sector)
 		rc = -EIO;
 		rc = -EIO;
 
 
 	if (rc) {
 	if (rc) {
-		printk(KERN_NOTICE PREFIX "'%s': unable to read header at "
+		printk(KERN_ERR PREFIX "'%s': unable to read header at "
 				"0x%lx\n", part->mbd.mtd->name,
 				"0x%lx\n", part->mbd.mtd->name,
 				part->blocks[block].offset);
 				part->blocks[block].offset);
 		goto err;
 		goto err;
@@ -602,7 +604,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr)
 		rc = -EIO;
 		rc = -EIO;
 
 
 	if (rc) {
 	if (rc) {
-		printk(KERN_WARNING PREFIX "error writing '%s' at "
+		printk(KERN_ERR PREFIX "error writing '%s' at "
 			"0x%lx\n", part->mbd.mtd->name, addr);
 			"0x%lx\n", part->mbd.mtd->name, addr);
 		if (rc)
 		if (rc)
 			goto err;
 			goto err;
@@ -652,7 +654,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
 	if (part->current_block == -1 ||
 	if (part->current_block == -1 ||
 		!part->blocks[part->current_block].free_sectors) {
 		!part->blocks[part->current_block].free_sectors) {
 
 
-		rc = find_writeable_block(part, old_addr);
+		rc = find_writable_block(part, old_addr);
 		if (rc)
 		if (rc)
 			goto err;
 			goto err;
 	}
 	}
@@ -675,7 +677,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
 		rc = -EIO;
 		rc = -EIO;
 
 
 	if (rc) {
 	if (rc) {
-		printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n",
+		printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
 				part->mbd.mtd->name, addr);
 				part->mbd.mtd->name, addr);
 		if (rc)
 		if (rc)
 			goto err;
 			goto err;
@@ -695,7 +697,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
 		rc = -EIO;
 		rc = -EIO;
 
 
 	if (rc) {
 	if (rc) {
-		printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n",
+		printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
 				part->mbd.mtd->name, addr);
 				part->mbd.mtd->name, addr);
 		if (rc)
 		if (rc)
 			goto err;
 			goto err;
@@ -776,7 +778,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 		part->block_size = block_size;
 		part->block_size = block_size;
 	else {
 	else {
 		if (!mtd->erasesize) {
 		if (!mtd->erasesize) {
-			printk(KERN_NOTICE PREFIX "please provide block_size");
+			printk(KERN_WARNING PREFIX "please provide block_size");
 			return;
 			return;
 		}
 		}
 		else
 		else
@@ -791,8 +793,8 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 		if (!(mtd->flags & MTD_WRITEABLE))
 		if (!(mtd->flags & MTD_WRITEABLE))
 			part->mbd.readonly = 1;
 			part->mbd.readonly = 1;
 		else if (part->errors) {
 		else if (part->errors) {
-			printk(KERN_NOTICE PREFIX "'%s': errors found, "
-					"setting read-only", mtd->name);
+			printk(KERN_WARNING PREFIX "'%s': errors found, "
+					"setting read-only\n", mtd->name);
 			part->mbd.readonly = 1;
 			part->mbd.readonly = 1;
 		}
 		}
 
 

+ 38 - 0
fs/Kconfig

@@ -1101,6 +1101,44 @@ config JFFS2_SUMMARY
 
 
 	  If unsure, say 'N'.
 	  If unsure, say 'N'.
 
 
+config JFFS2_FS_XATTR
+	bool "JFFS2 XATTR support (EXPERIMENTAL)"
+	depends on JFFS2_FS && EXPERIMENTAL && !JFFS2_FS_WRITEBUFFER
+	default n
+	help
+	  Extended attributes are name:value pairs associated with inodes by
+	  the kernel or by users (see the attr(5) manual page, or visit
+	  <http://acl.bestbits.at/> for details).
+	  
+	  If unsure, say N.
+
+config JFFS2_FS_POSIX_ACL
+	bool "JFFS2 POSIX Access Control Lists"
+	depends on JFFS2_FS_XATTR
+	default y
+	select FS_POSIX_ACL
+	help
+	  Posix Access Control Lists (ACLs) support permissions for users and
+	  groups beyond the owner/group/world scheme.
+	  
+	  To learn more about Access Control Lists, visit the Posix ACLs for
+	  Linux website <http://acl.bestbits.at/>.
+	  
+	  If you don't know what Access Control Lists are, say N
+
+config JFFS2_FS_SECURITY
+	bool "JFFS2 Security Labels"
+	depends on JFFS2_FS_XATTR
+	default y
+	help
+	  Security labels support alternative access control models
+	  implemented by security modules like SELinux.  This option
+	  enables an extended attribute handler for file security
+	  labels in the jffs2 filesystem.
+	  
+	  If you are not using a security module that requires using
+	  extended attributes for file security labels, say N.
+
 config JFFS2_COMPRESSION_OPTIONS
 config JFFS2_COMPRESSION_OPTIONS
 	bool "Advanced compression options for JFFS2"
 	bool "Advanced compression options for JFFS2"
 	depends on JFFS2_FS
 	depends on JFFS2_FS

+ 8 - 7
fs/jffs/intrep.c

@@ -247,7 +247,7 @@ flash_safe_read(struct mtd_info *mtd, loff_t from,
 	D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n",
 	D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n",
 		  mtd, (unsigned int) from, buf, count));
 		  mtd, (unsigned int) from, buf, count));
 
 
-	res = MTD_READ(mtd, from, count, &retlen, buf);
+	res = mtd->read(mtd, from, count, &retlen, buf);
 	if (retlen != count) {
 	if (retlen != count) {
 		panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res);
 		panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res);
 	}
 	}
@@ -262,7 +262,7 @@ flash_read_u32(struct mtd_info *mtd, loff_t from)
 	__u32 ret;
 	__u32 ret;
 	int res;
 	int res;
 
 
-	res = MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret);
+	res = mtd->read(mtd, from, 4, &retlen, (unsigned char *)&ret);
 	if (retlen != 4) {
 	if (retlen != 4) {
 		printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res);
 		printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res);
 		return 0;
 		return 0;
@@ -282,7 +282,7 @@ flash_safe_write(struct mtd_info *mtd, loff_t to,
 	D3(printk(KERN_NOTICE "flash_safe_write(%p, %08x, %p, %08x)\n",
 	D3(printk(KERN_NOTICE "flash_safe_write(%p, %08x, %p, %08x)\n",
 		  mtd, (unsigned int) to, buf, count));
 		  mtd, (unsigned int) to, buf, count));
 
 
-	res = MTD_WRITE(mtd, to, count, &retlen, buf);
+	res = mtd->write(mtd, to, count, &retlen, buf);
 	if (retlen != count) {
 	if (retlen != count) {
 		printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res);
 		printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res);
 	}
 	}
@@ -300,9 +300,9 @@ flash_safe_writev(struct mtd_info *mtd, const struct kvec *vecs,
 
 
 	D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n",
 	D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n",
 		  mtd, (unsigned int) to, vecs));
 		  mtd, (unsigned int) to, vecs));
-	
+
 	if (mtd->writev) {
 	if (mtd->writev) {
-		res = MTD_WRITEV(mtd, vecs, iovec_cnt, to, &retlen);
+		res = mtd->writev(mtd, vecs, iovec_cnt, to, &retlen);
 		return res ? res : retlen;
 		return res ? res : retlen;
 	}
 	}
 	/* Not implemented writev. Repeatedly use write - on the not so
 	/* Not implemented writev. Repeatedly use write - on the not so
@@ -312,7 +312,8 @@ flash_safe_writev(struct mtd_info *mtd, const struct kvec *vecs,
 	retlen=0;
 	retlen=0;
 
 
 	for (i=0; !res && i<iovec_cnt; i++) {
 	for (i=0; !res && i<iovec_cnt; i++) {
-		res = MTD_WRITE(mtd, to, vecs[i].iov_len, &retlen_a, vecs[i].iov_base);
+		res = mtd->write(mtd, to, vecs[i].iov_len, &retlen_a,
+				 vecs[i].iov_base);
 		if (retlen_a != vecs[i].iov_len) {
 		if (retlen_a != vecs[i].iov_len) {
 			printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res);
 			printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res);
 			if (i != iovec_cnt-1)
 			if (i != iovec_cnt-1)
@@ -393,7 +394,7 @@ flash_erase_region(struct mtd_info *mtd, loff_t start,
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	add_wait_queue(&wait_q, &wait);
 	add_wait_queue(&wait_q, &wait);
 
 
-	if (MTD_ERASE(mtd, erase) < 0) {
+	if (mtd->erase(mtd, erase) < 0) {
 		set_current_state(TASK_RUNNING);
 		set_current_state(TASK_RUNNING);
 		remove_wait_queue(&wait_q, &wait);
 		remove_wait_queue(&wait_q, &wait);
 		kfree(erase);
 		kfree(erase);

+ 3 - 0
fs/jffs2/Makefile

@@ -12,6 +12,9 @@ jffs2-y	+= symlink.o build.o erase.o background.o fs.o writev.o
 jffs2-y	+= super.o debug.o
 jffs2-y	+= super.o debug.o
 
 
 jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER)	+= wbuf.o
 jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER)	+= wbuf.o
+jffs2-$(CONFIG_JFFS2_FS_XATTR)		+= xattr.o xattr_trusted.o xattr_user.o
+jffs2-$(CONFIG_JFFS2_FS_SECURITY)	+= security.o
+jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL)	+= acl.o
 jffs2-$(CONFIG_JFFS2_RUBIN)	+= compr_rubin.o
 jffs2-$(CONFIG_JFFS2_RUBIN)	+= compr_rubin.o
 jffs2-$(CONFIG_JFFS2_RTIME)	+= compr_rtime.o
 jffs2-$(CONFIG_JFFS2_RTIME)	+= compr_rtime.o
 jffs2-$(CONFIG_JFFS2_ZLIB)	+= compr_zlib.o
 jffs2-$(CONFIG_JFFS2_ZLIB)	+= compr_zlib.o

+ 21 - 0
fs/jffs2/README.Locking

@@ -150,3 +150,24 @@ the buffer.
 
 
 Ordering constraints:
 Ordering constraints:
 	Lock wbuf_sem last, after the alloc_sem or and f->sem.
 	Lock wbuf_sem last, after the alloc_sem or and f->sem.
+
+
+	c->xattr_sem
+	------------
+
+This read/write semaphore protects against concurrent access to the
+xattr related objects which include stuff in superblock and ic->xref.
+In read-only path, write-semaphore is too much exclusion. It's enough
+by read-semaphore. But you must hold write-semaphore when updating,
+creating or deleting any xattr related object.
+
+Once xattr_sem released, there would be no assurance for the existence
+of those objects. Thus, a series of processes is often required to retry,
+when updating such a object is necessary under holding read semaphore.
+For example, do_jffs2_getxattr() holds read-semaphore to scan xref and
+xdatum at first. But it retries this process with holding write-semaphore
+after release read-semaphore, if it's necessary to load name/value pair
+from medium.
+
+Ordering constraints:
+	Lock xattr_sem last, after the alloc_sem.

+ 485 - 0
fs/jffs2/acl.c

@@ -0,0 +1,485 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2006  NEC Corporation
+ *
+ * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/crc32.h>
+#include <linux/jffs2.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+static size_t jffs2_acl_size(int count)
+{
+	if (count <= 4) {
+		return sizeof(struct jffs2_acl_header)
+		       + count * sizeof(struct jffs2_acl_entry_short);
+	} else {
+		return sizeof(struct jffs2_acl_header)
+		       + 4 * sizeof(struct jffs2_acl_entry_short)
+		       + (count - 4) * sizeof(struct jffs2_acl_entry);
+	}
+}
+
+static int jffs2_acl_count(size_t size)
+{
+	size_t s;
+
+	size -= sizeof(struct jffs2_acl_header);
+	s = size - 4 * sizeof(struct jffs2_acl_entry_short);
+	if (s < 0) {
+		if (size % sizeof(struct jffs2_acl_entry_short))
+			return -1;
+		return size / sizeof(struct jffs2_acl_entry_short);
+	} else {
+		if (s % sizeof(struct jffs2_acl_entry))
+			return -1;
+		return s / sizeof(struct jffs2_acl_entry) + 4;
+	}
+}
+
+static struct posix_acl *jffs2_acl_from_medium(void *value, size_t size)
+{
+	void *end = value + size;
+	struct jffs2_acl_header *header = value;
+	struct jffs2_acl_entry *entry;
+	struct posix_acl *acl;
+	uint32_t ver;
+	int i, count;
+
+	if (!value)
+		return NULL;
+	if (size < sizeof(struct jffs2_acl_header))
+		return ERR_PTR(-EINVAL);
+	ver = je32_to_cpu(header->a_version);
+	if (ver != JFFS2_ACL_VERSION) {
+		JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
+		return ERR_PTR(-EINVAL);
+	}
+
+	value += sizeof(struct jffs2_acl_header);
+	count = jffs2_acl_count(size);
+	if (count < 0)
+		return ERR_PTR(-EINVAL);
+	if (count == 0)
+		return NULL;
+
+	acl = posix_acl_alloc(count, GFP_KERNEL);
+	if (!acl)
+		return ERR_PTR(-ENOMEM);
+
+	for (i=0; i < count; i++) {
+		entry = value;
+		if (value + sizeof(struct jffs2_acl_entry_short) > end)
+			goto fail;
+		acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag);
+		acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm);
+		switch (acl->a_entries[i].e_tag) {
+			case ACL_USER_OBJ:
+			case ACL_GROUP_OBJ:
+			case ACL_MASK:
+			case ACL_OTHER:
+				value += sizeof(struct jffs2_acl_entry_short);
+				acl->a_entries[i].e_id = ACL_UNDEFINED_ID;
+				break;
+
+			case ACL_USER:
+			case ACL_GROUP:
+				value += sizeof(struct jffs2_acl_entry);
+				if (value > end)
+					goto fail;
+				acl->a_entries[i].e_id = je32_to_cpu(entry->e_id);
+				break;
+
+			default:
+				goto fail;
+		}
+	}
+	if (value != end)
+		goto fail;
+	return acl;
+ fail:
+	posix_acl_release(acl);
+	return ERR_PTR(-EINVAL);
+}
+
+static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
+{
+	struct jffs2_acl_header *header;
+	struct jffs2_acl_entry *entry;
+	void *e;
+	size_t i;
+
+	*size = jffs2_acl_size(acl->a_count);
+	header = kmalloc(sizeof(*header) + acl->a_count * sizeof(*entry), GFP_KERNEL);
+	if (!header)
+		return ERR_PTR(-ENOMEM);
+	header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
+	e = header + 1;
+	for (i=0; i < acl->a_count; i++) {
+		entry = e;
+		entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag);
+		entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm);
+		switch(acl->a_entries[i].e_tag) {
+			case ACL_USER:
+			case ACL_GROUP:
+				entry->e_id = cpu_to_je32(acl->a_entries[i].e_id);
+				e += sizeof(struct jffs2_acl_entry);
+				break;
+
+			case ACL_USER_OBJ:
+			case ACL_GROUP_OBJ:
+			case ACL_MASK:
+			case ACL_OTHER:
+				e += sizeof(struct jffs2_acl_entry_short);
+				break;
+
+			default:
+				goto fail;
+		}
+	}
+	return header;
+ fail:
+	kfree(header);
+	return ERR_PTR(-EINVAL);
+}
+
+static struct posix_acl *jffs2_iget_acl(struct inode *inode, struct posix_acl **i_acl)
+{
+	struct posix_acl *acl = JFFS2_ACL_NOT_CACHED;
+
+	spin_lock(&inode->i_lock);
+	if (*i_acl != JFFS2_ACL_NOT_CACHED)
+		acl = posix_acl_dup(*i_acl);
+	spin_unlock(&inode->i_lock);
+	return acl;
+}
+
+static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct posix_acl *acl)
+{
+	spin_lock(&inode->i_lock);
+	if (*i_acl != JFFS2_ACL_NOT_CACHED)
+		posix_acl_release(*i_acl);
+	*i_acl = posix_acl_dup(acl);
+	spin_unlock(&inode->i_lock);
+}
+
+static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
+{
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+	struct posix_acl *acl;
+	char *value = NULL;
+	int rc, xprefix;
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		acl = jffs2_iget_acl(inode, &f->i_acl_access);
+		if (acl != JFFS2_ACL_NOT_CACHED)
+			return acl;
+		xprefix = JFFS2_XPREFIX_ACL_ACCESS;
+		break;
+	case ACL_TYPE_DEFAULT:
+		acl = jffs2_iget_acl(inode, &f->i_acl_default);
+		if (acl != JFFS2_ACL_NOT_CACHED)
+			return acl;
+		xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
+		break;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+	rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0);
+	if (rc > 0) {
+		value = kmalloc(rc, GFP_KERNEL);
+		if (!value)
+			return ERR_PTR(-ENOMEM);
+		rc = do_jffs2_getxattr(inode, xprefix, "", value, rc);
+	}
+	if (rc > 0) {
+		acl = jffs2_acl_from_medium(value, rc);
+	} else if (rc == -ENODATA || rc == -ENOSYS) {
+		acl = NULL;
+	} else {
+		acl = ERR_PTR(rc);
+	}
+	if (value)
+		kfree(value);
+	if (!IS_ERR(acl)) {
+		switch (type) {
+		case ACL_TYPE_ACCESS:
+			jffs2_iset_acl(inode, &f->i_acl_access, acl);
+			break;
+		case ACL_TYPE_DEFAULT:
+			jffs2_iset_acl(inode, &f->i_acl_default, acl);
+			break;
+		}
+	}
+	return acl;
+}
+
+static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+	size_t size = 0;
+	char *value = NULL;
+	int rc, xprefix;
+
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		xprefix = JFFS2_XPREFIX_ACL_ACCESS;
+		if (acl) {
+			mode_t mode = inode->i_mode;
+			rc = posix_acl_equiv_mode(acl, &mode);
+			if (rc < 0)
+				return rc;
+			if (inode->i_mode != mode) {
+				inode->i_mode = mode;
+				jffs2_dirty_inode(inode);
+			}
+			if (rc == 0)
+				acl = NULL;
+		}
+		break;
+	case ACL_TYPE_DEFAULT:
+		xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
+		if (!S_ISDIR(inode->i_mode))
+			return acl ? -EACCES : 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (acl) {
+		value = jffs2_acl_to_medium(acl, &size);
+		if (IS_ERR(value))
+			return PTR_ERR(value);
+	}
+
+	rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
+	if (value)
+		kfree(value);
+	if (!rc) {
+		switch(type) {
+		case ACL_TYPE_ACCESS:
+			jffs2_iset_acl(inode, &f->i_acl_access, acl);
+			break;
+		case ACL_TYPE_DEFAULT:
+			jffs2_iset_acl(inode, &f->i_acl_default, acl);
+			break;
+		}
+	}
+	return rc;
+}
+
+static int jffs2_check_acl(struct inode *inode, int mask)
+{
+	struct posix_acl *acl;
+	int rc;
+
+	acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (acl) {
+		rc = posix_acl_permission(inode, acl, mask);
+		posix_acl_release(acl);
+		return rc;
+	}
+	return -EAGAIN;
+}
+
+int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	return generic_permission(inode, mask, jffs2_check_acl);
+}
+
+int jffs2_init_acl(struct inode *inode, struct inode *dir)
+{
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+	struct posix_acl *acl = NULL, *clone;
+	mode_t mode;
+	int rc = 0;
+
+	f->i_acl_access = JFFS2_ACL_NOT_CACHED;
+	f->i_acl_default = JFFS2_ACL_NOT_CACHED;
+	if (!S_ISLNK(inode->i_mode)) {
+		acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
+		if (IS_ERR(acl))
+			return PTR_ERR(acl);
+		if (!acl)
+			inode->i_mode &= ~current->fs->umask;
+	}
+	if (acl) {
+		if (S_ISDIR(inode->i_mode)) {
+			rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
+			if (rc)
+				goto cleanup;
+		}
+		clone = posix_acl_clone(acl, GFP_KERNEL);
+		rc = -ENOMEM;
+		if (!clone)
+			goto cleanup;
+		mode = inode->i_mode;
+		rc = posix_acl_create_masq(clone, &mode);
+		if (rc >= 0) {
+			inode->i_mode = mode;
+			if (rc > 0)
+				rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
+		}
+		posix_acl_release(clone);
+	}
+ cleanup:
+	posix_acl_release(acl);
+	return rc;
+}
+
+void jffs2_clear_acl(struct inode *inode)
+{
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+
+	if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) {
+		posix_acl_release(f->i_acl_access);
+		f->i_acl_access = JFFS2_ACL_NOT_CACHED;
+	}
+	if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) {
+		posix_acl_release(f->i_acl_default);
+		f->i_acl_default = JFFS2_ACL_NOT_CACHED;
+	}
+}
+
+int jffs2_acl_chmod(struct inode *inode)
+{
+	struct posix_acl *acl, *clone;
+	int rc;
+
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+	acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
+	if (IS_ERR(acl) || !acl)
+		return PTR_ERR(acl);
+	clone = posix_acl_clone(acl, GFP_KERNEL);
+	posix_acl_release(acl);
+	if (!clone)
+		return -ENOMEM;
+	rc = posix_acl_chmod_masq(clone, inode->i_mode);
+	if (!rc)
+		rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
+	posix_acl_release(clone);
+	return rc;
+}
+
+static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size,
+					 const char *name, size_t name_len)
+{
+	const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS);
+
+	if (list && retlen <= list_size)
+		strcpy(list, POSIX_ACL_XATTR_ACCESS);
+	return retlen;
+}
+
+static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size,
+					  const char *name, size_t name_len)
+{
+	const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT);
+
+	if (list && retlen <= list_size)
+		strcpy(list, POSIX_ACL_XATTR_DEFAULT);
+	return retlen;
+}
+
+static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size)
+{
+	struct posix_acl *acl;
+	int rc;
+
+	acl = jffs2_get_acl(inode, type);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (!acl)
+		return -ENODATA;
+	rc = posix_acl_to_xattr(acl, buffer, size);
+	posix_acl_release(acl);
+
+	return rc;
+}
+
+static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
+{
+	if (name[0] != '\0')
+		return -EINVAL;
+	return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size);
+}
+
+static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
+{
+	if (name[0] != '\0')
+		return -EINVAL;
+	return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
+}
+
+static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size)
+{
+	struct posix_acl *acl;
+	int rc;
+
+	if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+		return -EPERM;
+
+	if (value) {
+		acl = posix_acl_from_xattr(value, size);
+		if (IS_ERR(acl))
+			return PTR_ERR(acl);
+		if (acl) {
+			rc = posix_acl_valid(acl);
+			if (rc)
+				goto out;
+		}
+	} else {
+		acl = NULL;
+	}
+	rc = jffs2_set_acl(inode, type, acl);
+ out:
+	posix_acl_release(acl);
+	return rc;
+}
+
+static int jffs2_acl_access_setxattr(struct inode *inode, const char *name,
+				     const void *buffer, size_t size, int flags)
+{
+	if (name[0] != '\0')
+		return -EINVAL;
+	return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size);
+}
+
+static int jffs2_acl_default_setxattr(struct inode *inode, const char *name,
+				      const void *buffer, size_t size, int flags)
+{
+	if (name[0] != '\0')
+		return -EINVAL;
+	return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
+}
+
+struct xattr_handler jffs2_acl_access_xattr_handler = {
+	.prefix	= POSIX_ACL_XATTR_ACCESS,
+	.list	= jffs2_acl_access_listxattr,
+	.get	= jffs2_acl_access_getxattr,
+	.set	= jffs2_acl_access_setxattr,
+};
+
+struct xattr_handler jffs2_acl_default_xattr_handler = {
+	.prefix	= POSIX_ACL_XATTR_DEFAULT,
+	.list	= jffs2_acl_default_listxattr,
+	.get	= jffs2_acl_default_getxattr,
+	.set	= jffs2_acl_default_setxattr,
+};

+ 45 - 0
fs/jffs2/acl.h

@@ -0,0 +1,45 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2006  NEC Corporation
+ *
+ * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+struct jffs2_acl_entry {
+	jint16_t	e_tag;
+	jint16_t	e_perm;
+	jint32_t	e_id;
+};
+
+struct jffs2_acl_entry_short {
+	jint16_t	e_tag;
+	jint16_t	e_perm;
+};
+
+struct jffs2_acl_header {
+	jint32_t	a_version;
+};
+
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+
+#define JFFS2_ACL_NOT_CACHED ((void *)-1)
+
+extern int jffs2_permission(struct inode *, int, struct nameidata *);
+extern int jffs2_acl_chmod(struct inode *);
+extern int jffs2_init_acl(struct inode *, struct inode *);
+extern void jffs2_clear_acl(struct inode *);
+
+extern struct xattr_handler jffs2_acl_access_xattr_handler;
+extern struct xattr_handler jffs2_acl_default_xattr_handler;
+
+#else
+
+#define jffs2_permission NULL
+#define jffs2_acl_chmod(inode)		(0)
+#define jffs2_init_acl(inode,dir)	(0)
+#define jffs2_clear_acl(inode)
+
+#endif	/* CONFIG_JFFS2_FS_POSIX_ACL */

+ 2 - 0
fs/jffs2/build.c

@@ -160,6 +160,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
 		ic->scan_dents = NULL;
 		ic->scan_dents = NULL;
 		cond_resched();
 		cond_resched();
 	}
 	}
+	jffs2_build_xattr_subsystem(c);
 	c->flags &= ~JFFS2_SB_FLAG_BUILDING;
 	c->flags &= ~JFFS2_SB_FLAG_BUILDING;
 
 
 	dbg_fsbuild("FS build complete\n");
 	dbg_fsbuild("FS build complete\n");
@@ -178,6 +179,7 @@ exit:
 				jffs2_free_full_dirent(fd);
 				jffs2_free_full_dirent(fd);
 			}
 			}
 		}
 		}
+		jffs2_clear_xattr_subsystem(c);
 	}
 	}
 
 
 	return ret;
 	return ret;

+ 1 - 1
fs/jffs2/compr.c

@@ -412,7 +412,7 @@ void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
                 kfree(comprbuf);
                 kfree(comprbuf);
 }
 }
 
 
-int jffs2_compressors_init(void)
+int __init jffs2_compressors_init(void)
 {
 {
 /* Registering compressors */
 /* Registering compressors */
 #ifdef CONFIG_JFFS2_ZLIB
 #ifdef CONFIG_JFFS2_ZLIB

+ 2 - 2
fs/jffs2/compr.h

@@ -23,8 +23,8 @@
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/jffs2.h>
 #include <linux/jffs2.h>
-#include <linux/jffs2_fs_i.h>
-#include <linux/jffs2_fs_sb.h>
+#include "jffs2_fs_i.h"
+#include "jffs2_fs_sb.h"
 #include "nodelist.h"
 #include "nodelist.h"
 
 
 #define JFFS2_RUBINMIPS_PRIORITY 10
 #define JFFS2_RUBINMIPS_PRIORITY 10

+ 7 - 7
fs/jffs2/debug.c

@@ -192,13 +192,13 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
 		else
 		else
 			my_dirty_size += totlen;
 			my_dirty_size += totlen;
 
 
-		if ((!ref2->next_phys) != (ref2 == jeb->last_node)) {
-			JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next_phys at %#08x (mem %p), last_node is at %#08x (mem %p).\n",
-				ref_offset(ref2), ref2, ref_offset(ref2->next_phys), ref2->next_phys,
-				ref_offset(jeb->last_node), jeb->last_node);
+		if ((!ref_next(ref2)) != (ref2 == jeb->last_node)) {
+			JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next at %#08x (mem %p), last_node is at %#08x (mem %p).\n",
+				    ref_offset(ref2), ref2, ref_offset(ref_next(ref2)), ref_next(ref2),
+				    ref_offset(jeb->last_node), jeb->last_node);
 			goto error;
 			goto error;
 		}
 		}
-		ref2 = ref2->next_phys;
+		ref2 = ref_next(ref2);
 	}
 	}
 
 
 	if (my_used_size != jeb->used_size) {
 	if (my_used_size != jeb->used_size) {
@@ -268,9 +268,9 @@ __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c,
 	}
 	}
 
 
 	printk(JFFS2_DBG);
 	printk(JFFS2_DBG);
-	for (ref = jeb->first_node; ; ref = ref->next_phys) {
+	for (ref = jeb->first_node; ; ref = ref_next(ref)) {
 		printk("%#08x(%#x)", ref_offset(ref), ref->__totlen);
 		printk("%#08x(%#x)", ref_offset(ref), ref->__totlen);
-		if (ref->next_phys)
+		if (ref_next(ref))
 			printk("->");
 			printk("->");
 		else
 		else
 			break;
 			break;

+ 6 - 0
fs/jffs2/debug.h

@@ -171,6 +171,12 @@
 #define dbg_memalloc(fmt, ...)
 #define dbg_memalloc(fmt, ...)
 #endif
 #endif
 
 
+/* Watch the XATTR subsystem */
+#ifdef JFFS2_DBG_XATTR_MESSAGES
+#define dbg_xattr(fmt, ...)  JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_xattr(fmt, ...)
+#endif 
 
 
 /* "Sanity" checks */
 /* "Sanity" checks */
 void
 void

+ 86 - 35
fs/jffs2/dir.c

@@ -17,8 +17,8 @@
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/crc32.h>
 #include <linux/crc32.h>
 #include <linux/jffs2.h>
 #include <linux/jffs2.h>
-#include <linux/jffs2_fs_i.h>
-#include <linux/jffs2_fs_sb.h>
+#include "jffs2_fs_i.h"
+#include "jffs2_fs_sb.h"
 #include <linux/time.h>
 #include <linux/time.h>
 #include "nodelist.h"
 #include "nodelist.h"
 
 
@@ -57,7 +57,12 @@ struct inode_operations jffs2_dir_inode_operations =
 	.rmdir =	jffs2_rmdir,
 	.rmdir =	jffs2_rmdir,
 	.mknod =	jffs2_mknod,
 	.mknod =	jffs2_mknod,
 	.rename =	jffs2_rename,
 	.rename =	jffs2_rename,
+	.permission =	jffs2_permission,
 	.setattr =	jffs2_setattr,
 	.setattr =	jffs2_setattr,
+	.setxattr =	jffs2_setxattr,
+	.getxattr =	jffs2_getxattr,
+	.listxattr =	jffs2_listxattr,
+	.removexattr =	jffs2_removexattr
 };
 };
 
 
 /***********************************************************************/
 /***********************************************************************/
@@ -78,6 +83,9 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
 
 
 	D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
 	D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
 
 
+	if (target->d_name.len > JFFS2_MAX_NAME_LEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	c = JFFS2_SB_INFO(dir_i->i_sb);
 	c = JFFS2_SB_INFO(dir_i->i_sb);
 
 
@@ -206,12 +214,15 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
 	ret = jffs2_do_create(c, dir_f, f, ri,
 	ret = jffs2_do_create(c, dir_f, f, ri,
 			      dentry->d_name.name, dentry->d_name.len);
 			      dentry->d_name.name, dentry->d_name.len);
 
 
-	if (ret) {
-		make_bad_inode(inode);
-		iput(inode);
-		jffs2_free_raw_inode(ri);
-		return ret;
-	}
+	if (ret)
+		goto fail;
+
+	ret = jffs2_init_security(inode, dir_i);
+	if (ret)
+		goto fail;
+	ret = jffs2_init_acl(inode, dir_i);
+	if (ret)
+		goto fail;
 
 
 	dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
 	dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
 
 
@@ -221,6 +232,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
 	D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
 	D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
 		  inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
 		  inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
 	return 0;
 	return 0;
+
+ fail:
+	make_bad_inode(inode);
+	iput(inode);
+	jffs2_free_raw_inode(ri);
+	return ret;
 }
 }
 
 
 /***********************************************************************/
 /***********************************************************************/
@@ -291,7 +308,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dirent *fd;
 	struct jffs2_full_dirent *fd;
 	int namelen;
 	int namelen;
-	uint32_t alloclen, phys_ofs;
+	uint32_t alloclen;
 	int ret, targetlen = strlen(target);
 	int ret, targetlen = strlen(target);
 
 
 	/* FIXME: If you care. We'd need to use frags for the target
 	/* FIXME: If you care. We'd need to use frags for the target
@@ -310,8 +327,8 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
 	 * Just the node will do for now, though
 	 * Just the node will do for now, though
 	 */
 	 */
 	namelen = dentry->d_name.len;
 	namelen = dentry->d_name.len;
-	ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen,
-				ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
+	ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &alloclen,
+				  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
 
 
 	if (ret) {
 	if (ret) {
 		jffs2_free_raw_inode(ri);
 		jffs2_free_raw_inode(ri);
@@ -339,7 +356,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
 	ri->data_crc = cpu_to_je32(crc32(0, target, targetlen));
 	ri->data_crc = cpu_to_je32(crc32(0, target, targetlen));
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 
 
-	fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL);
+	fn = jffs2_write_dnode(c, f, ri, target, targetlen, ALLOC_NORMAL);
 
 
 	jffs2_free_raw_inode(ri);
 	jffs2_free_raw_inode(ri);
 
 
@@ -371,8 +388,20 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
 	up(&f->sem);
 	up(&f->sem);
 
 
 	jffs2_complete_reservation(c);
 	jffs2_complete_reservation(c);
-	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
-				ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+
+	ret = jffs2_init_security(inode, dir_i);
+	if (ret) {
+		jffs2_clear_inode(inode);
+		return ret;
+	}
+	ret = jffs2_init_acl(inode, dir_i);
+	if (ret) {
+		jffs2_clear_inode(inode);
+		return ret;
+	}
+
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
+				  ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
 	if (ret) {
 	if (ret) {
 		/* Eep. */
 		/* Eep. */
 		jffs2_clear_inode(inode);
 		jffs2_clear_inode(inode);
@@ -404,7 +433,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
 	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
 
 
-	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL);
 
 
 	if (IS_ERR(fd)) {
 	if (IS_ERR(fd)) {
 		/* dirent failed to write. Delete the inode normally
 		/* dirent failed to write. Delete the inode normally
@@ -442,7 +471,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dirent *fd;
 	struct jffs2_full_dirent *fd;
 	int namelen;
 	int namelen;
-	uint32_t alloclen, phys_ofs;
+	uint32_t alloclen;
 	int ret;
 	int ret;
 
 
 	mode |= S_IFDIR;
 	mode |= S_IFDIR;
@@ -457,8 +486,8 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
 	 * Just the node will do for now, though
 	 * Just the node will do for now, though
 	 */
 	 */
 	namelen = dentry->d_name.len;
 	namelen = dentry->d_name.len;
-	ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL,
-				JFFS2_SUMMARY_INODE_SIZE);
+	ret = jffs2_reserve_space(c, sizeof(*ri), &alloclen, ALLOC_NORMAL,
+				  JFFS2_SUMMARY_INODE_SIZE);
 
 
 	if (ret) {
 	if (ret) {
 		jffs2_free_raw_inode(ri);
 		jffs2_free_raw_inode(ri);
@@ -483,7 +512,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
 	ri->data_crc = cpu_to_je32(0);
 	ri->data_crc = cpu_to_je32(0);
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 
 
-	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+	fn = jffs2_write_dnode(c, f, ri, NULL, 0, ALLOC_NORMAL);
 
 
 	jffs2_free_raw_inode(ri);
 	jffs2_free_raw_inode(ri);
 
 
@@ -501,8 +530,20 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
 	up(&f->sem);
 	up(&f->sem);
 
 
 	jffs2_complete_reservation(c);
 	jffs2_complete_reservation(c);
-	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
-				ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+
+	ret = jffs2_init_security(inode, dir_i);
+	if (ret) {
+		jffs2_clear_inode(inode);
+		return ret;
+	}
+	ret = jffs2_init_acl(inode, dir_i);
+	if (ret) {
+		jffs2_clear_inode(inode);
+		return ret;
+	}
+
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
+				  ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
 	if (ret) {
 	if (ret) {
 		/* Eep. */
 		/* Eep. */
 		jffs2_clear_inode(inode);
 		jffs2_clear_inode(inode);
@@ -534,7 +575,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
 	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
 
 
-	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL);
 
 
 	if (IS_ERR(fd)) {
 	if (IS_ERR(fd)) {
 		/* dirent failed to write. Delete the inode normally
 		/* dirent failed to write. Delete the inode normally
@@ -588,12 +629,12 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dirent *fd;
 	struct jffs2_full_dirent *fd;
 	int namelen;
 	int namelen;
-	jint16_t dev;
+	union jffs2_device_node dev;
 	int devlen = 0;
 	int devlen = 0;
-	uint32_t alloclen, phys_ofs;
+	uint32_t alloclen;
 	int ret;
 	int ret;
 
 
-	if (!old_valid_dev(rdev))
+	if (!new_valid_dev(rdev))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	ri = jffs2_alloc_raw_inode();
 	ri = jffs2_alloc_raw_inode();
@@ -602,17 +643,15 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
 
 
 	c = JFFS2_SB_INFO(dir_i->i_sb);
 	c = JFFS2_SB_INFO(dir_i->i_sb);
 
 
-	if (S_ISBLK(mode) || S_ISCHR(mode)) {
-		dev = cpu_to_je16(old_encode_dev(rdev));
-		devlen = sizeof(dev);
-	}
+	if (S_ISBLK(mode) || S_ISCHR(mode))
+		devlen = jffs2_encode_dev(&dev, rdev);
 
 
 	/* Try to reserve enough space for both node and dirent.
 	/* Try to reserve enough space for both node and dirent.
 	 * Just the node will do for now, though
 	 * Just the node will do for now, though
 	 */
 	 */
 	namelen = dentry->d_name.len;
 	namelen = dentry->d_name.len;
-	ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen,
-				ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
+	ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &alloclen,
+				  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
 
 
 	if (ret) {
 	if (ret) {
 		jffs2_free_raw_inode(ri);
 		jffs2_free_raw_inode(ri);
@@ -639,7 +678,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
 	ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
 	ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 
 
-	fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL);
+	fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, ALLOC_NORMAL);
 
 
 	jffs2_free_raw_inode(ri);
 	jffs2_free_raw_inode(ri);
 
 
@@ -657,8 +696,20 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
 	up(&f->sem);
 	up(&f->sem);
 
 
 	jffs2_complete_reservation(c);
 	jffs2_complete_reservation(c);
-	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
-				ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+
+	ret = jffs2_init_security(inode, dir_i);
+	if (ret) {
+		jffs2_clear_inode(inode);
+		return ret;
+	}
+	ret = jffs2_init_acl(inode, dir_i);
+	if (ret) {
+		jffs2_clear_inode(inode);
+		return ret;
+	}
+
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
+				  ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
 	if (ret) {
 	if (ret) {
 		/* Eep. */
 		/* Eep. */
 		jffs2_clear_inode(inode);
 		jffs2_clear_inode(inode);
@@ -693,7 +744,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
 	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
 
 
-	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL);
 
 
 	if (IS_ERR(fd)) {
 	if (IS_ERR(fd)) {
 		/* dirent failed to write. Delete the inode normally
 		/* dirent failed to write. Delete the inode normally

+ 22 - 34
fs/jffs2/erase.c

@@ -30,7 +30,6 @@ static void jffs2_erase_callback(struct erase_info *);
 #endif
 #endif
 static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
 static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
 static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 
 
 static void jffs2_erase_block(struct jffs2_sb_info *c,
 static void jffs2_erase_block(struct jffs2_sb_info *c,
@@ -136,7 +135,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
 			c->used_size -= jeb->used_size;
 			c->used_size -= jeb->used_size;
 			c->dirty_size -= jeb->dirty_size;
 			c->dirty_size -= jeb->dirty_size;
 			jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
 			jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
-			jffs2_free_all_node_refs(c, jeb);
+			jffs2_free_jeb_node_refs(c, jeb);
 			list_add(&jeb->list, &c->erasing_list);
 			list_add(&jeb->list, &c->erasing_list);
 			spin_unlock(&c->erase_completion_lock);
 			spin_unlock(&c->erase_completion_lock);
 
 
@@ -231,6 +230,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
 			   at the end of the linked list. Stash it and continue
 			   at the end of the linked list. Stash it and continue
 			   from the beginning of the list */
 			   from the beginning of the list */
 			ic = (struct jffs2_inode_cache *)(*prev);
 			ic = (struct jffs2_inode_cache *)(*prev);
+			BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE);
 			prev = &ic->nodes;
 			prev = &ic->nodes;
 			continue;
 			continue;
 		}
 		}
@@ -283,22 +283,27 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
 		jffs2_del_ino_cache(c, ic);
 		jffs2_del_ino_cache(c, ic);
 }
 }
 
 
-static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 {
-	struct jffs2_raw_node_ref *ref;
+	struct jffs2_raw_node_ref *block, *ref;
 	D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
 	D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
-	while(jeb->first_node) {
-		ref = jeb->first_node;
-		jeb->first_node = ref->next_phys;
 
 
-		/* Remove from the inode-list */
-		if (ref->next_in_ino)
+	block = ref = jeb->first_node;
+
+	while (ref) {
+		if (ref->flash_offset == REF_LINK_NODE) {
+			ref = ref->next_in_ino;
+			jffs2_free_refblock(block);
+			block = ref;
+			continue;
+		}
+		if (ref->flash_offset != REF_EMPTY_NODE && ref->next_in_ino)
 			jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
 			jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
 		/* else it was a non-inode node or already removed, so don't bother */
 		/* else it was a non-inode node or already removed, so don't bother */
 
 
-		jffs2_free_raw_node_ref(ref);
+		ref++;
 	}
 	}
-	jeb->last_node = NULL;
+	jeb->first_node = jeb->last_node = NULL;
 }
 }
 
 
 static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
 static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
@@ -351,7 +356,6 @@ fail:
 
 
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 {
-	struct jffs2_raw_node_ref *marker_ref = NULL;
 	size_t retlen;
 	size_t retlen;
 	int ret;
 	int ret;
 	uint32_t bad_offset;
 	uint32_t bad_offset;
@@ -373,12 +377,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
 				goto filebad;
 				goto filebad;
 		}
 		}
 
 
-		jeb->first_node = jeb->last_node = NULL;
+		/* Everything else got zeroed before the erase */
 		jeb->free_size = c->sector_size;
 		jeb->free_size = c->sector_size;
-		jeb->used_size = 0;
-		jeb->dirty_size = 0;
-		jeb->wasted_size = 0;
-
 	} else {
 	} else {
 
 
 		struct kvec vecs[1];
 		struct kvec vecs[1];
@@ -388,11 +388,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
 			.totlen =	cpu_to_je32(c->cleanmarker_size)
 			.totlen =	cpu_to_je32(c->cleanmarker_size)
 		};
 		};
 
 
-		marker_ref = jffs2_alloc_raw_node_ref();
-		if (!marker_ref) {
-			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
-			goto refile;
-		}
+		jffs2_prealloc_raw_node_refs(c, jeb, 1);
 
 
 		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
 		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
 
 
@@ -408,21 +404,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
 				printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
 				printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
 				       jeb->offset, sizeof(marker), retlen);
 				       jeb->offset, sizeof(marker), retlen);
 
 
-			jffs2_free_raw_node_ref(marker_ref);
 			goto filebad;
 			goto filebad;
 		}
 		}
 
 
-		marker_ref->next_in_ino = NULL;
-		marker_ref->next_phys = NULL;
-		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
-		marker_ref->__totlen = c->cleanmarker_size;
-
-		jeb->first_node = jeb->last_node = marker_ref;
-
-		jeb->free_size = c->sector_size - c->cleanmarker_size;
-		jeb->used_size = c->cleanmarker_size;
-		jeb->dirty_size = 0;
-		jeb->wasted_size = 0;
+		/* Everything else got zeroed before the erase */
+		jeb->free_size = c->sector_size;
+		/* FIXME Special case for cleanmarker in empty block */
+		jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);
 	}
 	}
 
 
 	spin_lock(&c->erase_completion_lock);
 	spin_lock(&c->erase_completion_lock);

+ 24 - 11
fs/jffs2/file.c

@@ -54,7 +54,12 @@ const struct file_operations jffs2_file_operations =
 
 
 struct inode_operations jffs2_file_inode_operations =
 struct inode_operations jffs2_file_inode_operations =
 {
 {
-	.setattr =	jffs2_setattr
+	.permission =	jffs2_permission,
+	.setattr =	jffs2_setattr,
+	.setxattr =	jffs2_setxattr,
+	.getxattr =	jffs2_getxattr,
+	.listxattr =	jffs2_listxattr,
+	.removexattr =	jffs2_removexattr
 };
 };
 
 
 struct address_space_operations jffs2_file_address_operations =
 struct address_space_operations jffs2_file_address_operations =
@@ -129,13 +134,13 @@ static int jffs2_prepare_write (struct file *filp, struct page *pg,
 		struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
 		struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
 		struct jffs2_raw_inode ri;
 		struct jffs2_raw_inode ri;
 		struct jffs2_full_dnode *fn;
 		struct jffs2_full_dnode *fn;
-		uint32_t phys_ofs, alloc_len;
+		uint32_t alloc_len;
 
 
 		D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
 		D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
 			  (unsigned int)inode->i_size, pageofs));
 			  (unsigned int)inode->i_size, pageofs));
 
 
-		ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len,
-					ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
+		ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
+					  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 
 
@@ -161,7 +166,7 @@ static int jffs2_prepare_write (struct file *filp, struct page *pg,
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 		ri.data_crc = cpu_to_je32(0);
 		ri.data_crc = cpu_to_je32(0);
 
 
-		fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+		fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_NORMAL);
 
 
 		if (IS_ERR(fn)) {
 		if (IS_ERR(fn)) {
 			ret = PTR_ERR(fn);
 			ret = PTR_ERR(fn);
@@ -215,12 +220,20 @@ static int jffs2_commit_write (struct file *filp, struct page *pg,
 	D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
 	D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
 		  inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
 		  inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
 
 
-	if (!start && end == PAGE_CACHE_SIZE) {
-		/* We need to avoid deadlock with page_cache_read() in
-		   jffs2_garbage_collect_pass(). So we have to mark the
-		   page up to date, to prevent page_cache_read() from
-		   trying to re-lock it. */
-		SetPageUptodate(pg);
+	if (end == PAGE_CACHE_SIZE) {
+		if (!start) {
+			/* We need to avoid deadlock with page_cache_read() in
+			   jffs2_garbage_collect_pass(). So we have to mark the
+			   page up to date, to prevent page_cache_read() from
+			   trying to re-lock it. */
+			SetPageUptodate(pg);
+		} else {
+			/* When writing out the end of a page, write out the 
+			   _whole_ page. This helps to reduce the number of
+			   nodes in files which have many short writes, like
+			   syslog files. */
+			start = aligned_start = 0;
+		}
 	}
 	}
 
 
 	ri = jffs2_alloc_raw_inode();
 	ri = jffs2_alloc_raw_inode();

+ 38 - 25
fs/jffs2/fs.c

@@ -33,11 +33,11 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
 	struct jffs2_raw_inode *ri;
 	struct jffs2_raw_inode *ri;
-	unsigned short dev;
+	union jffs2_device_node dev;
 	unsigned char *mdata = NULL;
 	unsigned char *mdata = NULL;
 	int mdatalen = 0;
 	int mdatalen = 0;
 	unsigned int ivalid;
 	unsigned int ivalid;
-	uint32_t phys_ofs, alloclen;
+	uint32_t alloclen;
 	int ret;
 	int ret;
 	D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
 	D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
 	ret = inode_change_ok(inode, iattr);
 	ret = inode_change_ok(inode, iattr);
@@ -51,20 +51,24 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
 	   it out again with the appropriate data attached */
 	   it out again with the appropriate data attached */
 	if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
 	if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
 		/* For these, we don't actually need to read the old node */
 		/* For these, we don't actually need to read the old node */
-		dev = old_encode_dev(inode->i_rdev);
+		mdatalen = jffs2_encode_dev(&dev, inode->i_rdev);
 		mdata = (char *)&dev;
 		mdata = (char *)&dev;
-		mdatalen = sizeof(dev);
 		D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
 		D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
 	} else if (S_ISLNK(inode->i_mode)) {
 	} else if (S_ISLNK(inode->i_mode)) {
+		down(&f->sem);
 		mdatalen = f->metadata->size;
 		mdatalen = f->metadata->size;
 		mdata = kmalloc(f->metadata->size, GFP_USER);
 		mdata = kmalloc(f->metadata->size, GFP_USER);
-		if (!mdata)
+		if (!mdata) {
+			up(&f->sem);
 			return -ENOMEM;
 			return -ENOMEM;
+		}
 		ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
 		ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
 		if (ret) {
 		if (ret) {
+			up(&f->sem);
 			kfree(mdata);
 			kfree(mdata);
 			return ret;
 			return ret;
 		}
 		}
+		up(&f->sem);
 		D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
 		D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
 	}
 	}
 
 
@@ -75,8 +79,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen,
-				ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
+	ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &alloclen,
+				  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
 	if (ret) {
 	if (ret) {
 		jffs2_free_raw_inode(ri);
 		jffs2_free_raw_inode(ri);
 		if (S_ISLNK(inode->i_mode & S_IFMT))
 		if (S_ISLNK(inode->i_mode & S_IFMT))
@@ -127,7 +131,7 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
 	else
 	else
 		ri->data_crc = cpu_to_je32(0);
 		ri->data_crc = cpu_to_je32(0);
 
 
-	new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL);
+	new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, ALLOC_NORMAL);
 	if (S_ISLNK(inode->i_mode))
 	if (S_ISLNK(inode->i_mode))
 		kfree(mdata);
 		kfree(mdata);
 
 
@@ -180,7 +184,12 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
 
 
 int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
 int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
 {
 {
-	return jffs2_do_setattr(dentry->d_inode, iattr);
+	int rc;
+
+	rc = jffs2_do_setattr(dentry->d_inode, iattr);
+	if (!rc && (iattr->ia_valid & ATTR_MODE))
+		rc = jffs2_acl_chmod(dentry->d_inode);
+	return rc;
 }
 }
 
 
 int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
 int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
@@ -219,6 +228,7 @@ void jffs2_clear_inode (struct inode *inode)
 
 
 	D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
 	D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
 
 
+	jffs2_xattr_delete_inode(c, f->inocache);
 	jffs2_do_clear_inode(c, f);
 	jffs2_do_clear_inode(c, f);
 }
 }
 
 
@@ -227,6 +237,8 @@ void jffs2_read_inode (struct inode *inode)
 	struct jffs2_inode_info *f;
 	struct jffs2_inode_info *f;
 	struct jffs2_sb_info *c;
 	struct jffs2_sb_info *c;
 	struct jffs2_raw_inode latest_node;
 	struct jffs2_raw_inode latest_node;
+	union jffs2_device_node jdev;
+	dev_t rdev = 0;
 	int ret;
 	int ret;
 
 
 	D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
 	D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
@@ -258,7 +270,6 @@ void jffs2_read_inode (struct inode *inode)
 	inode->i_blocks = (inode->i_size + 511) >> 9;
 	inode->i_blocks = (inode->i_size + 511) >> 9;
 
 
 	switch (inode->i_mode & S_IFMT) {
 	switch (inode->i_mode & S_IFMT) {
-		jint16_t rdev;
 
 
 	case S_IFLNK:
 	case S_IFLNK:
 		inode->i_op = &jffs2_symlink_inode_operations;
 		inode->i_op = &jffs2_symlink_inode_operations;
@@ -292,8 +303,16 @@ void jffs2_read_inode (struct inode *inode)
 	case S_IFBLK:
 	case S_IFBLK:
 	case S_IFCHR:
 	case S_IFCHR:
 		/* Read the device numbers from the media */
 		/* Read the device numbers from the media */
+		if (f->metadata->size != sizeof(jdev.old) &&
+		    f->metadata->size != sizeof(jdev.new)) {
+			printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size);
+			up(&f->sem);
+			jffs2_do_clear_inode(c, f);
+			make_bad_inode(inode);
+			return;
+		}
 		D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
 		D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
-		if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
+		if (jffs2_read_dnode(c, f, f->metadata, (char *)&jdev, 0, f->metadata->size) < 0) {
 			/* Eep */
 			/* Eep */
 			printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
 			printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
 			up(&f->sem);
 			up(&f->sem);
@@ -301,12 +320,15 @@ void jffs2_read_inode (struct inode *inode)
 			make_bad_inode(inode);
 			make_bad_inode(inode);
 			return;
 			return;
 		}
 		}
+		if (f->metadata->size == sizeof(jdev.old))
+			rdev = old_decode_dev(je16_to_cpu(jdev.old));
+		else
+			rdev = new_decode_dev(je32_to_cpu(jdev.new));
 
 
 	case S_IFSOCK:
 	case S_IFSOCK:
 	case S_IFIFO:
 	case S_IFIFO:
 		inode->i_op = &jffs2_file_inode_operations;
 		inode->i_op = &jffs2_file_inode_operations;
-		init_special_inode(inode, inode->i_mode,
-				   old_decode_dev((je16_to_cpu(rdev))));
+		init_special_inode(inode, inode->i_mode, rdev);
 		break;
 		break;
 
 
 	default:
 	default:
@@ -492,6 +514,8 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
 	}
 	}
 	memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
 	memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
 
 
+	jffs2_init_xattr_subsystem(c);
+
 	if ((ret = jffs2_do_mount_fs(c)))
 	if ((ret = jffs2_do_mount_fs(c)))
 		goto out_inohash;
 		goto out_inohash;
 
 
@@ -526,6 +550,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
 	else
 	else
 		kfree(c->blocks);
 		kfree(c->blocks);
  out_inohash:
  out_inohash:
+	jffs2_clear_xattr_subsystem(c);
 	kfree(c->inocache_list);
 	kfree(c->inocache_list);
  out_wbuf:
  out_wbuf:
 	jffs2_flash_cleanup(c);
 	jffs2_flash_cleanup(c);
@@ -639,13 +664,6 @@ static int jffs2_flash_setup(struct jffs2_sb_info *c) {
 			return ret;
 			return ret;
 	}
 	}
 
 
-	/* add setups for other bizarre flashes here... */
-	if (jffs2_nor_ecc(c)) {
-		ret = jffs2_nor_ecc_flash_setup(c);
-		if (ret)
-			return ret;
-	}
-
 	/* and Dataflash */
 	/* and Dataflash */
 	if (jffs2_dataflash(c)) {
 	if (jffs2_dataflash(c)) {
 		ret = jffs2_dataflash_setup(c);
 		ret = jffs2_dataflash_setup(c);
@@ -669,11 +687,6 @@ void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
 		jffs2_nand_flash_cleanup(c);
 		jffs2_nand_flash_cleanup(c);
 	}
 	}
 
 
-	/* add cleanups for other bizarre flashes here... */
-	if (jffs2_nor_ecc(c)) {
-		jffs2_nor_ecc_flash_cleanup(c);
-	}
-
 	/* and DataFlash */
 	/* and DataFlash */
 	if (jffs2_dataflash(c)) {
 	if (jffs2_dataflash(c)) {
 		jffs2_dataflash_cleanup(c);
 		jffs2_dataflash_cleanup(c);

+ 67 - 64
fs/jffs2/gc.c

@@ -125,6 +125,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_raw_node_ref *raw;
 	int ret = 0, inum, nlink;
 	int ret = 0, inum, nlink;
+	int xattr = 0;
 
 
 	if (down_interruptible(&c->alloc_sem))
 	if (down_interruptible(&c->alloc_sem))
 		return -EINTR;
 		return -EINTR;
@@ -138,7 +139,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 		   the node CRCs etc. Do it now. */
 		   the node CRCs etc. Do it now. */
 
 
 		/* checked_ino is protected by the alloc_sem */
 		/* checked_ino is protected by the alloc_sem */
-		if (c->checked_ino > c->highest_ino) {
+		if (c->checked_ino > c->highest_ino && xattr) {
 			printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
 			printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
 			       c->unchecked_size);
 			       c->unchecked_size);
 			jffs2_dbg_dump_block_lists_nolock(c);
 			jffs2_dbg_dump_block_lists_nolock(c);
@@ -148,6 +149,9 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 
 
 		spin_unlock(&c->erase_completion_lock);
 		spin_unlock(&c->erase_completion_lock);
 
 
+		if (!xattr)
+			xattr = jffs2_verify_xattr(c);
+
 		spin_lock(&c->inocache_lock);
 		spin_lock(&c->inocache_lock);
 
 
 		ic = jffs2_get_ino_cache(c, c->checked_ino++);
 		ic = jffs2_get_ino_cache(c, c->checked_ino++);
@@ -181,6 +185,10 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 			   and trigger the BUG() above while we haven't yet
 			   and trigger the BUG() above while we haven't yet
 			   finished checking all its nodes */
 			   finished checking all its nodes */
 			D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
 			D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
+			/* We need to come back again for the _same_ inode. We've
+			 made no progress in this case, but that should be OK */
+			c->checked_ino--;
+
 			up(&c->alloc_sem);
 			up(&c->alloc_sem);
 			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
 			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
 			return 0;
 			return 0;
@@ -231,7 +239,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 
 
 	while(ref_obsolete(raw)) {
 	while(ref_obsolete(raw)) {
 		D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
 		D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
-		raw = raw->next_phys;
+		raw = ref_next(raw);
 		if (unlikely(!raw)) {
 		if (unlikely(!raw)) {
 			printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
 			printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
 			printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
 			printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
@@ -248,16 +256,37 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 
 
 	if (!raw->next_in_ino) {
 	if (!raw->next_in_ino) {
 		/* Inode-less node. Clean marker, snapshot or something like that */
 		/* Inode-less node. Clean marker, snapshot or something like that */
-		/* FIXME: If it's something that needs to be copied, including something
-		   we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
 		spin_unlock(&c->erase_completion_lock);
 		spin_unlock(&c->erase_completion_lock);
-		jffs2_mark_node_obsolete(c, raw);
+		if (ref_flags(raw) == REF_PRISTINE) {
+			/* It's an unknown node with JFFS2_FEATURE_RWCOMPAT_COPY */
+			jffs2_garbage_collect_pristine(c, NULL, raw);
+		} else {
+			/* Just mark it obsolete */
+			jffs2_mark_node_obsolete(c, raw);
+		}
 		up(&c->alloc_sem);
 		up(&c->alloc_sem);
 		goto eraseit_lock;
 		goto eraseit_lock;
 	}
 	}
 
 
 	ic = jffs2_raw_ref_to_ic(raw);
 	ic = jffs2_raw_ref_to_ic(raw);
 
 
+#ifdef CONFIG_JFFS2_FS_XATTR
+	/* When 'ic' refers xattr_datum/xattr_ref, this node is GCed as xattr.
+	 * We can decide whether this node is inode or xattr by ic->class.     */
+	if (ic->class == RAWNODE_CLASS_XATTR_DATUM
+	    || ic->class == RAWNODE_CLASS_XATTR_REF) {
+		BUG_ON(raw->next_in_ino != (void *)ic);
+		spin_unlock(&c->erase_completion_lock);
+
+		if (ic->class == RAWNODE_CLASS_XATTR_DATUM) {
+			ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+		} else {
+			ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+		}
+		goto release_sem;
+	}
+#endif
+
 	/* We need to hold the inocache. Either the erase_completion_lock or
 	/* We need to hold the inocache. Either the erase_completion_lock or
 	   the inocache_lock are sufficient; we trade down since the inocache_lock
 	   the inocache_lock are sufficient; we trade down since the inocache_lock
 	   causes less contention. */
 	   causes less contention. */
@@ -499,7 +528,6 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 					  struct jffs2_raw_node_ref *raw)
 					  struct jffs2_raw_node_ref *raw)
 {
 {
 	union jffs2_node_union *node;
 	union jffs2_node_union *node;
-	struct jffs2_raw_node_ref *nraw;
 	size_t retlen;
 	size_t retlen;
 	int ret;
 	int ret;
 	uint32_t phys_ofs, alloclen;
 	uint32_t phys_ofs, alloclen;
@@ -508,15 +536,16 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 
 
 	D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
 	D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
 
 
-	rawlen = ref_totlen(c, c->gcblock, raw);
+	alloclen = rawlen = ref_totlen(c, c->gcblock, raw);
 
 
 	/* Ask for a small amount of space (or the totlen if smaller) because we
 	/* Ask for a small amount of space (or the totlen if smaller) because we
 	   don't want to force wastage of the end of a block if splitting would
 	   don't want to force wastage of the end of a block if splitting would
 	   work. */
 	   work. */
-	ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) +
-				JFFS2_MIN_DATA_LEN, rawlen), &phys_ofs, &alloclen, rawlen);
-				/* this is not the exact summary size of it,
-					it is only an upper estimation */
+	if (ic && alloclen > sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN)
+		alloclen = sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN;
+
+	ret = jffs2_reserve_space_gc(c, alloclen, &alloclen, rawlen);
+	/* 'rawlen' is not the exact summary size; it is only an upper estimation */
 
 
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
@@ -580,22 +609,17 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 		}
 		}
 		break;
 		break;
 	default:
 	default:
-		printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
-		       ref_offset(raw), je16_to_cpu(node->u.nodetype));
-		goto bail;
-	}
-
-	nraw = jffs2_alloc_raw_node_ref();
-	if (!nraw) {
-		ret = -ENOMEM;
-		goto out_node;
+		/* If it's inode-less, we don't _know_ what it is. Just copy it intact */
+		if (ic) {
+			printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
+			       ref_offset(raw), je16_to_cpu(node->u.nodetype));
+			goto bail;
+		}
 	}
 	}
 
 
 	/* OK, all the CRCs are good; this node can just be copied as-is. */
 	/* OK, all the CRCs are good; this node can just be copied as-is. */
  retry:
  retry:
-	nraw->flash_offset = phys_ofs;
-	nraw->__totlen = rawlen;
-	nraw->next_phys = NULL;
+	phys_ofs = write_ofs(c);
 
 
 	ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
 	ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
 
 
@@ -603,17 +627,11 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
 		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
                        rawlen, phys_ofs, ret, retlen);
                        rawlen, phys_ofs, ret, retlen);
 		if (retlen) {
 		if (retlen) {
-                        /* Doesn't belong to any inode */
-			nraw->next_in_ino = NULL;
-
-			nraw->flash_offset |= REF_OBSOLETE;
-			jffs2_add_physical_node_ref(c, nraw);
-			jffs2_mark_node_obsolete(c, nraw);
+			jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL);
 		} else {
 		} else {
-			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
-                        jffs2_free_raw_node_ref(nraw);
+			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", phys_ofs);
 		}
 		}
-		if (!retried && (nraw = jffs2_alloc_raw_node_ref())) {
+		if (!retried) {
 			/* Try to reallocate space and retry */
 			/* Try to reallocate space and retry */
 			uint32_t dummy;
 			uint32_t dummy;
 			struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
 			struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
@@ -625,7 +643,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 			jffs2_dbg_acct_sanity_check(c,jeb);
 			jffs2_dbg_acct_sanity_check(c,jeb);
 			jffs2_dbg_acct_paranoia_check(c, jeb);
 			jffs2_dbg_acct_paranoia_check(c, jeb);
 
 
-			ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy, rawlen);
+			ret = jffs2_reserve_space_gc(c, rawlen, &dummy, rawlen);
 						/* this is not the exact summary size of it,
 						/* this is not the exact summary size of it,
 							it is only an upper estimation */
 							it is only an upper estimation */
 
 
@@ -638,25 +656,13 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 				goto retry;
 				goto retry;
 			}
 			}
 			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
 			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
-			jffs2_free_raw_node_ref(nraw);
 		}
 		}
 
 
-		jffs2_free_raw_node_ref(nraw);
 		if (!ret)
 		if (!ret)
 			ret = -EIO;
 			ret = -EIO;
 		goto out_node;
 		goto out_node;
 	}
 	}
-	nraw->flash_offset |= REF_PRISTINE;
-	jffs2_add_physical_node_ref(c, nraw);
-
-	/* Link into per-inode list. This is safe because of the ic
-	   state being INO_STATE_GC. Note that if we're doing this
-	   for an inode which is in-core, the 'nraw' pointer is then
-	   going to be fetched from ic->nodes by our caller. */
-	spin_lock(&c->erase_completion_lock);
-        nraw->next_in_ino = ic->nodes;
-        ic->nodes = nraw;
-	spin_unlock(&c->erase_completion_lock);
+	jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, rawlen, ic);
 
 
 	jffs2_mark_node_obsolete(c, raw);
 	jffs2_mark_node_obsolete(c, raw);
 	D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
 	D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
@@ -675,19 +681,16 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_raw_inode ri;
 	struct jffs2_raw_inode ri;
 	struct jffs2_node_frag *last_frag;
 	struct jffs2_node_frag *last_frag;
-	jint16_t dev;
+	union jffs2_device_node dev;
 	char *mdata = NULL, mdatalen = 0;
 	char *mdata = NULL, mdatalen = 0;
-	uint32_t alloclen, phys_ofs, ilen;
+	uint32_t alloclen, ilen;
 	int ret;
 	int ret;
 
 
 	if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
 	if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
 	    S_ISCHR(JFFS2_F_I_MODE(f)) ) {
 	    S_ISCHR(JFFS2_F_I_MODE(f)) ) {
 		/* For these, we don't actually need to read the old node */
 		/* For these, we don't actually need to read the old node */
-		/* FIXME: for minor or major > 255. */
-		dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) |
-			JFFS2_F_I_RDEV_MIN(f)));
+		mdatalen = jffs2_encode_dev(&dev, JFFS2_F_I_RDEV(f));
 		mdata = (char *)&dev;
 		mdata = (char *)&dev;
-		mdatalen = sizeof(dev);
 		D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
 		D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
 	} else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
 	} else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
 		mdatalen = fn->size;
 		mdatalen = fn->size;
@@ -706,7 +709,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
 
 
 	}
 	}
 
 
-	ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen,
+	ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &alloclen,
 				JFFS2_SUMMARY_INODE_SIZE);
 				JFFS2_SUMMARY_INODE_SIZE);
 	if (ret) {
 	if (ret) {
 		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
 		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
@@ -744,7 +747,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
 	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 	ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
 	ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
 
 
-	new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC);
+	new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, ALLOC_GC);
 
 
 	if (IS_ERR(new_fn)) {
 	if (IS_ERR(new_fn)) {
 		printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
 		printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
@@ -765,7 +768,7 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
 {
 {
 	struct jffs2_full_dirent *new_fd;
 	struct jffs2_full_dirent *new_fd;
 	struct jffs2_raw_dirent rd;
 	struct jffs2_raw_dirent rd;
-	uint32_t alloclen, phys_ofs;
+	uint32_t alloclen;
 	int ret;
 	int ret;
 
 
 	rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
 	rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -787,14 +790,14 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
 	rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
 	rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
 	rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
 	rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
 
 
-	ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen,
+	ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &alloclen,
 				JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize));
 				JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize));
 	if (ret) {
 	if (ret) {
 		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
 		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
 		       sizeof(rd)+rd.nsize, ret);
 		       sizeof(rd)+rd.nsize, ret);
 		return ret;
 		return ret;
 	}
 	}
-	new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC);
+	new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, ALLOC_GC);
 
 
 	if (IS_ERR(new_fd)) {
 	if (IS_ERR(new_fd)) {
 		printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
 		printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
@@ -922,7 +925,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
 	struct jffs2_raw_inode ri;
 	struct jffs2_raw_inode ri;
 	struct jffs2_node_frag *frag;
 	struct jffs2_node_frag *frag;
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_full_dnode *new_fn;
-	uint32_t alloclen, phys_ofs, ilen;
+	uint32_t alloclen, ilen;
 	int ret;
 	int ret;
 
 
 	D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
 	D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
@@ -1001,14 +1004,14 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
 	ri.data_crc = cpu_to_je32(0);
 	ri.data_crc = cpu_to_je32(0);
 	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 
 
-	ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen,
-				JFFS2_SUMMARY_INODE_SIZE);
+	ret = jffs2_reserve_space_gc(c, sizeof(ri), &alloclen,
+				     JFFS2_SUMMARY_INODE_SIZE);
 	if (ret) {
 	if (ret) {
 		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
 		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
 		       sizeof(ri), ret);
 		       sizeof(ri), ret);
 		return ret;
 		return ret;
 	}
 	}
-	new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
+	new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_GC);
 
 
 	if (IS_ERR(new_fn)) {
 	if (IS_ERR(new_fn)) {
 		printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
 		printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
@@ -1070,7 +1073,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
 {
 {
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_raw_inode ri;
 	struct jffs2_raw_inode ri;
-	uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;
+	uint32_t alloclen, offset, orig_end, orig_start;
 	int ret = 0;
 	int ret = 0;
 	unsigned char *comprbuf = NULL, *writebuf;
 	unsigned char *comprbuf = NULL, *writebuf;
 	unsigned long pg;
 	unsigned long pg;
@@ -1227,7 +1230,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
 		uint32_t cdatalen;
 		uint32_t cdatalen;
 		uint16_t comprtype = JFFS2_COMPR_NONE;
 		uint16_t comprtype = JFFS2_COMPR_NONE;
 
 
-		ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs,
+		ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN,
 					&alloclen, JFFS2_SUMMARY_INODE_SIZE);
 					&alloclen, JFFS2_SUMMARY_INODE_SIZE);
 
 
 		if (ret) {
 		if (ret) {
@@ -1264,7 +1267,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 		ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 		ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 
 
-		new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
+		new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, ALLOC_GC);
 
 
 		jffs2_free_comprbuf(comprbuf, writebuf);
 		jffs2_free_comprbuf(comprbuf, writebuf);
 
 

+ 0 - 3
fs/jffs2/histo.h

@@ -1,3 +0,0 @@
-/* This file provides the bit-probabilities for the input file */
-#define BIT_DIVIDER 629
-static int bits[9] = { 179,167,183,165,159,198,178,119,}; /* ia32 .so files */

+ 5 - 0
include/linux/jffs2_fs_i.h → fs/jffs2/jffs2_fs_i.h

@@ -5,6 +5,7 @@
 
 
 #include <linux/version.h>
 #include <linux/version.h>
 #include <linux/rbtree.h>
 #include <linux/rbtree.h>
+#include <linux/posix_acl.h>
 #include <asm/semaphore.h>
 #include <asm/semaphore.h>
 
 
 struct jffs2_inode_info {
 struct jffs2_inode_info {
@@ -45,6 +46,10 @@ struct jffs2_inode_info {
 	struct inode vfs_inode;
 	struct inode vfs_inode;
 #endif
 #endif
 #endif
 #endif
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+	struct posix_acl *i_acl_access;
+	struct posix_acl *i_acl_default;
+#endif
 };
 };
 
 
 #endif /* _JFFS2_FS_I */
 #endif /* _JFFS2_FS_I */

+ 12 - 1
include/linux/jffs2_fs_sb.h → fs/jffs2/jffs2_fs_sb.h

@@ -100,6 +100,7 @@ struct jffs2_sb_info {
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	/* Write-behind buffer for NAND flash */
 	/* Write-behind buffer for NAND flash */
 	unsigned char *wbuf;
 	unsigned char *wbuf;
+	unsigned char *oobbuf;
 	uint32_t wbuf_ofs;
 	uint32_t wbuf_ofs;
 	uint32_t wbuf_len;
 	uint32_t wbuf_len;
 	struct jffs2_inodirty *wbuf_inodes;
 	struct jffs2_inodirty *wbuf_inodes;
@@ -107,7 +108,7 @@ struct jffs2_sb_info {
 	struct rw_semaphore wbuf_sem;	/* Protects the write buffer */
 	struct rw_semaphore wbuf_sem;	/* Protects the write buffer */
 
 
 	/* Information about out-of-band area usage... */
 	/* Information about out-of-band area usage... */
-	struct nand_oobinfo *oobinfo;
+	struct nand_ecclayout *ecclayout;
 	uint32_t badblock_pos;
 	uint32_t badblock_pos;
 	uint32_t fsdata_pos;
 	uint32_t fsdata_pos;
 	uint32_t fsdata_len;
 	uint32_t fsdata_len;
@@ -115,6 +116,16 @@ struct jffs2_sb_info {
 
 
 	struct jffs2_summary *summary;		/* Summary information */
 	struct jffs2_summary *summary;		/* Summary information */
 
 
+#ifdef CONFIG_JFFS2_FS_XATTR
+#define XATTRINDEX_HASHSIZE	(57)
+	uint32_t highest_xid;
+	struct list_head xattrindex[XATTRINDEX_HASHSIZE];
+	struct list_head xattr_unchecked;
+	struct jffs2_xattr_ref *xref_temp;
+	struct rw_semaphore xattr_sem;
+	uint32_t xdatum_mem_usage;
+	uint32_t xdatum_mem_threshold;
+#endif
 	/* OS-private pointer for getting back to master superblock info */
 	/* OS-private pointer for getting back to master superblock info */
 	void *os_priv;
 	void *os_priv;
 };
 };

+ 120 - 7
fs/jffs2/malloc.c

@@ -26,6 +26,10 @@ static kmem_cache_t *tmp_dnode_info_slab;
 static kmem_cache_t *raw_node_ref_slab;
 static kmem_cache_t *raw_node_ref_slab;
 static kmem_cache_t *node_frag_slab;
 static kmem_cache_t *node_frag_slab;
 static kmem_cache_t *inode_cache_slab;
 static kmem_cache_t *inode_cache_slab;
+#ifdef CONFIG_JFFS2_FS_XATTR
+static kmem_cache_t *xattr_datum_cache;
+static kmem_cache_t *xattr_ref_cache;
+#endif
 
 
 int __init jffs2_create_slab_caches(void)
 int __init jffs2_create_slab_caches(void)
 {
 {
@@ -53,8 +57,8 @@ int __init jffs2_create_slab_caches(void)
 	if (!tmp_dnode_info_slab)
 	if (!tmp_dnode_info_slab)
 		goto err;
 		goto err;
 
 
-	raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
-					      sizeof(struct jffs2_raw_node_ref),
+	raw_node_ref_slab = kmem_cache_create("jffs2_refblock",
+					      sizeof(struct jffs2_raw_node_ref) * (REFS_PER_BLOCK + 1),
 					      0, 0, NULL, NULL);
 					      0, 0, NULL, NULL);
 	if (!raw_node_ref_slab)
 	if (!raw_node_ref_slab)
 		goto err;
 		goto err;
@@ -68,8 +72,24 @@ int __init jffs2_create_slab_caches(void)
 	inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
 	inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
 					     sizeof(struct jffs2_inode_cache),
 					     sizeof(struct jffs2_inode_cache),
 					     0, 0, NULL, NULL);
 					     0, 0, NULL, NULL);
-	if (inode_cache_slab)
-		return 0;
+	if (!inode_cache_slab)
+		goto err;
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+	xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum",
+					     sizeof(struct jffs2_xattr_datum),
+					     0, 0, NULL, NULL);
+	if (!xattr_datum_cache)
+		goto err;
+
+	xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref",
+					   sizeof(struct jffs2_xattr_ref),
+					   0, 0, NULL, NULL);
+	if (!xattr_ref_cache)
+		goto err;
+#endif
+
+	return 0;
  err:
  err:
 	jffs2_destroy_slab_caches();
 	jffs2_destroy_slab_caches();
 	return -ENOMEM;
 	return -ENOMEM;
@@ -91,6 +111,12 @@ void jffs2_destroy_slab_caches(void)
 		kmem_cache_destroy(node_frag_slab);
 		kmem_cache_destroy(node_frag_slab);
 	if(inode_cache_slab)
 	if(inode_cache_slab)
 		kmem_cache_destroy(inode_cache_slab);
 		kmem_cache_destroy(inode_cache_slab);
+#ifdef CONFIG_JFFS2_FS_XATTR
+	if (xattr_datum_cache)
+		kmem_cache_destroy(xattr_datum_cache);
+	if (xattr_ref_cache)
+		kmem_cache_destroy(xattr_ref_cache);
+#endif
 }
 }
 
 
 struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
 struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
@@ -164,15 +190,65 @@ void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
 	kmem_cache_free(tmp_dnode_info_slab, x);
 	kmem_cache_free(tmp_dnode_info_slab, x);
 }
 }
 
 
-struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
+struct jffs2_raw_node_ref *jffs2_alloc_refblock(void)
 {
 {
 	struct jffs2_raw_node_ref *ret;
 	struct jffs2_raw_node_ref *ret;
+
 	ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
 	ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
-	dbg_memalloc("%p\n", ret);
+	if (ret) {
+		int i = 0;
+		for (i=0; i < REFS_PER_BLOCK; i++) {
+			ret[i].flash_offset = REF_EMPTY_NODE;
+			ret[i].next_in_ino = NULL;
+		}
+		ret[i].flash_offset = REF_LINK_NODE;
+		ret[i].next_in_ino = NULL;
+	}
 	return ret;
 	return ret;
 }
 }
 
 
-void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
+int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
+				 struct jffs2_eraseblock *jeb, int nr)
+{
+	struct jffs2_raw_node_ref **p, *ref;
+	int i = nr;
+
+	dbg_memalloc("%d\n", nr);
+
+	p = &jeb->last_node;
+	ref = *p;
+
+	dbg_memalloc("Reserving %d refs for block @0x%08x\n", nr, jeb->offset);
+
+	/* If jeb->last_node is really a valid node then skip over it */
+	if (ref && ref->flash_offset != REF_EMPTY_NODE)
+		ref++;
+
+	while (i) {
+		if (!ref) {
+			dbg_memalloc("Allocating new refblock linked from %p\n", p);
+			ref = *p = jffs2_alloc_refblock();
+			if (!ref)
+				return -ENOMEM;
+		}
+		if (ref->flash_offset == REF_LINK_NODE) {
+			p = &ref->next_in_ino;
+			ref = *p;
+			continue;
+		}
+		i--;
+		ref++;
+	}
+	jeb->allocated_refs = nr;
+
+	dbg_memalloc("Reserved %d refs for block @0x%08x, last_node is %p (%08x,%p)\n",
+		  nr, jeb->offset, jeb->last_node, jeb->last_node->flash_offset,
+		  jeb->last_node->next_in_ino);
+
+	return 0;
+}
+
+void jffs2_free_refblock(struct jffs2_raw_node_ref *x)
 {
 {
 	dbg_memalloc("%p\n", x);
 	dbg_memalloc("%p\n", x);
 	kmem_cache_free(raw_node_ref_slab, x);
 	kmem_cache_free(raw_node_ref_slab, x);
@@ -205,3 +281,40 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
 	dbg_memalloc("%p\n", x);
 	dbg_memalloc("%p\n", x);
 	kmem_cache_free(inode_cache_slab, x);
 	kmem_cache_free(inode_cache_slab, x);
 }
 }
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
+{
+	struct jffs2_xattr_datum *xd;
+	xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL);
+	dbg_memalloc("%p\n", xd);
+
+	memset(xd, 0, sizeof(struct jffs2_xattr_datum));
+	xd->class = RAWNODE_CLASS_XATTR_DATUM;
+	INIT_LIST_HEAD(&xd->xindex);
+	return xd;
+}
+
+void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd)
+{
+	dbg_memalloc("%p\n", xd);
+	kmem_cache_free(xattr_datum_cache, xd);
+}
+
+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
+{
+	struct jffs2_xattr_ref *ref;
+	ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL);
+	dbg_memalloc("%p\n", ref);
+
+	memset(ref, 0, sizeof(struct jffs2_xattr_ref));
+	ref->class = RAWNODE_CLASS_XATTR_REF;
+	return ref;
+}
+
+void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref)
+{
+	dbg_memalloc("%p\n", ref);
+	kmem_cache_free(xattr_ref_cache, ref);
+}
+#endif

+ 176 - 7
fs/jffs2/nodelist.c

@@ -438,8 +438,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
 	if (c->mtd->point) {
 	if (c->mtd->point) {
 		err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
 		err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
 		if (!err && retlen < tn->csize) {
 		if (!err && retlen < tn->csize) {
-			JFFS2_WARNING("MTD point returned len too short: %zu "
-					"instead of %u.\n", retlen, tn->csize);
+			JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
 			c->mtd->unpoint(c->mtd, buffer, ofs, len);
 			c->mtd->unpoint(c->mtd, buffer, ofs, len);
 		} else if (err)
 		} else if (err)
 			JFFS2_WARNING("MTD point failed: error code %d.\n", err);
 			JFFS2_WARNING("MTD point failed: error code %d.\n", err);
@@ -462,8 +461,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
 		}
 		}
 
 
 		if (retlen != len) {
 		if (retlen != len) {
-			JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n",
-					ofs, retlen, len);
+			JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len);
 			err = -EIO;
 			err = -EIO;
 			goto free_out;
 			goto free_out;
 		}
 		}
@@ -940,6 +938,7 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
 		this = c->inocache_list[i];
 		this = c->inocache_list[i];
 		while (this) {
 		while (this) {
 			next = this->next;
 			next = this->next;
+			jffs2_xattr_free_inode(c, this);
 			jffs2_free_inode_cache(this);
 			jffs2_free_inode_cache(this);
 			this = next;
 			this = next;
 		}
 		}
@@ -954,9 +953,13 @@ void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
 
 
 	for (i=0; i<c->nr_blocks; i++) {
 	for (i=0; i<c->nr_blocks; i++) {
 		this = c->blocks[i].first_node;
 		this = c->blocks[i].first_node;
-		while(this) {
-			next = this->next_phys;
-			jffs2_free_raw_node_ref(this);
+		while (this) {
+			if (this[REFS_PER_BLOCK].flash_offset == REF_LINK_NODE)
+				next = this[REFS_PER_BLOCK].next_in_ino;
+			else
+				next = NULL;
+
+			jffs2_free_refblock(this);
 			this = next;
 			this = next;
 		}
 		}
 		c->blocks[i].first_node = c->blocks[i].last_node = NULL;
 		c->blocks[i].first_node = c->blocks[i].last_node = NULL;
@@ -1047,3 +1050,169 @@ void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
 		cond_resched();
 		cond_resched();
 	}
 	}
 }
 }
+
+struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
+					       struct jffs2_eraseblock *jeb,
+					       uint32_t ofs, uint32_t len,
+					       struct jffs2_inode_cache *ic)
+{
+	struct jffs2_raw_node_ref *ref;
+
+	BUG_ON(!jeb->allocated_refs);
+	jeb->allocated_refs--;
+
+	ref = jeb->last_node;
+
+	dbg_noderef("Last node at %p is (%08x,%p)\n", ref, ref->flash_offset,
+		    ref->next_in_ino);
+
+	while (ref->flash_offset != REF_EMPTY_NODE) {
+		if (ref->flash_offset == REF_LINK_NODE)
+			ref = ref->next_in_ino;
+		else
+			ref++;
+	}
+
+	dbg_noderef("New ref is %p (%08x becomes %08x,%p) len 0x%x\n", ref, 
+		    ref->flash_offset, ofs, ref->next_in_ino, len);
+
+	ref->flash_offset = ofs;
+
+	if (!jeb->first_node) {
+		jeb->first_node = ref;
+		BUG_ON(ref_offset(ref) != jeb->offset);
+	} else if (unlikely(ref_offset(ref) != jeb->offset + c->sector_size - jeb->free_size)) {
+		uint32_t last_len = ref_totlen(c, jeb, jeb->last_node);
+
+		JFFS2_ERROR("Adding new ref %p at (0x%08x-0x%08x) not immediately after previous (0x%08x-0x%08x)\n",
+			    ref, ref_offset(ref), ref_offset(ref)+len,
+			    ref_offset(jeb->last_node), 
+			    ref_offset(jeb->last_node)+last_len);
+		BUG();
+	}
+	jeb->last_node = ref;
+
+	if (ic) {
+		ref->next_in_ino = ic->nodes;
+		ic->nodes = ref;
+	} else {
+		ref->next_in_ino = NULL;
+	}
+
+	switch(ref_flags(ref)) {
+	case REF_UNCHECKED:
+		c->unchecked_size += len;
+		jeb->unchecked_size += len;
+		break;
+
+	case REF_NORMAL:
+	case REF_PRISTINE:
+		c->used_size += len;
+		jeb->used_size += len;
+		break;
+
+	case REF_OBSOLETE:
+		c->dirty_size += len;
+		jeb->dirty_size += len;
+		break;
+	}
+	c->free_size -= len;
+	jeb->free_size -= len;
+
+#ifdef TEST_TOTLEN
+	/* Set (and test) __totlen field... for now */
+	ref->__totlen = len;
+	ref_totlen(c, jeb, ref);
+#endif
+	return ref;
+}
+
+/* No locking, no reservation of 'ref'. Do not use on a live file system */
+int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+			   uint32_t size)
+{
+	if (!size)
+		return 0;
+	if (unlikely(size > jeb->free_size)) {
+		printk(KERN_CRIT "Dirty space 0x%x larger then free_size 0x%x (wasted 0x%x)\n",
+		       size, jeb->free_size, jeb->wasted_size);
+		BUG();
+	}
+	/* REF_EMPTY_NODE is !obsolete, so that works OK */
+	if (jeb->last_node && ref_obsolete(jeb->last_node)) {
+#ifdef TEST_TOTLEN
+		jeb->last_node->__totlen += size;
+#endif
+		c->dirty_size += size;
+		c->free_size -= size;
+		jeb->dirty_size += size;
+		jeb->free_size -= size;
+	} else {
+		uint32_t ofs = jeb->offset + c->sector_size - jeb->free_size;
+		ofs |= REF_OBSOLETE;
+
+		jffs2_link_node_ref(c, jeb, ofs, size, NULL);
+	}
+
+	return 0;
+}
+
+/* Calculate totlen from surrounding nodes or eraseblock */
+static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
+				    struct jffs2_eraseblock *jeb,
+				    struct jffs2_raw_node_ref *ref)
+{
+	uint32_t ref_end;
+	struct jffs2_raw_node_ref *next_ref = ref_next(ref);
+
+	if (next_ref)
+		ref_end = ref_offset(next_ref);
+	else {
+		if (!jeb)
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+
+		/* Last node in block. Use free_space */
+		if (unlikely(ref != jeb->last_node)) {
+			printk(KERN_CRIT "ref %p @0x%08x is not jeb->last_node (%p @0x%08x)\n",
+			       ref, ref_offset(ref), jeb->last_node, jeb->last_node?ref_offset(jeb->last_node):0);
+			BUG();
+		}
+		ref_end = jeb->offset + c->sector_size - jeb->free_size;
+	}
+	return ref_end - ref_offset(ref);
+}
+
+uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+			    struct jffs2_raw_node_ref *ref)
+{
+	uint32_t ret;
+
+	ret = __ref_totlen(c, jeb, ref);
+
+#ifdef TEST_TOTLEN
+	if (unlikely(ret != ref->__totlen)) {
+		if (!jeb)
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+
+		printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
+		       ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
+		       ret, ref->__totlen);
+		if (ref_next(ref)) {
+			printk(KERN_CRIT "next %p (0x%08x-0x%08x)\n", ref_next(ref), ref_offset(ref_next(ref)),
+			       ref_offset(ref_next(ref))+ref->__totlen);
+		} else 
+			printk(KERN_CRIT "No next ref. jeb->last_node is %p\n", jeb->last_node);
+
+		printk(KERN_CRIT "jeb->wasted_size %x, dirty_size %x, used_size %x, free_size %x\n", jeb->wasted_size, jeb->dirty_size, jeb->used_size, jeb->free_size);
+
+#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
+		__jffs2_dbg_dump_node_refs_nolock(c, jeb);
+#endif
+
+		WARN_ON(1);
+
+		ret = ref->__totlen;
+	}
+#endif /* TEST_TOTLEN */
+	return ret;
+}

+ 115 - 74
fs/jffs2/nodelist.h

@@ -18,8 +18,10 @@
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/jffs2.h>
 #include <linux/jffs2.h>
-#include <linux/jffs2_fs_sb.h>
-#include <linux/jffs2_fs_i.h>
+#include "jffs2_fs_sb.h"
+#include "jffs2_fs_i.h"
+#include "xattr.h"
+#include "acl.h"
 #include "summary.h"
 #include "summary.h"
 
 
 #ifdef __ECOS
 #ifdef __ECOS
@@ -75,14 +77,50 @@
 struct jffs2_raw_node_ref
 struct jffs2_raw_node_ref
 {
 {
 	struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref
 	struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref
-		for this inode. If this is the last, it points to the inode_cache
-		for this inode instead. The inode_cache will have NULL in the first
-		word so you know when you've got there :) */
-	struct jffs2_raw_node_ref *next_phys;
+		for this object. If this _is_ the last, it points to the inode_cache,
+		xattr_ref or xattr_datum instead. The common part of those structures
+		has NULL in the first word. See jffs2_raw_ref_to_ic() below */
 	uint32_t flash_offset;
 	uint32_t flash_offset;
+#define TEST_TOTLEN
+#ifdef TEST_TOTLEN
 	uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
 	uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
+#endif
 };
 };
 
 
+#define REF_LINK_NODE ((int32_t)-1)
+#define REF_EMPTY_NODE ((int32_t)-2)
+
+/* Use blocks of about 256 bytes */
+#define REFS_PER_BLOCK ((255/sizeof(struct jffs2_raw_node_ref))-1)
+
+static inline struct jffs2_raw_node_ref *ref_next(struct jffs2_raw_node_ref *ref)
+{
+	ref++;
+
+	/* Link to another block of refs */
+	if (ref->flash_offset == REF_LINK_NODE) {
+		ref = ref->next_in_ino;
+		if (!ref)
+			return ref;
+	}
+
+	/* End of chain */
+	if (ref->flash_offset == REF_EMPTY_NODE)
+		return NULL;
+
+	return ref;
+}
+
+static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
+{
+	while(raw->next_in_ino)
+		raw = raw->next_in_ino;
+
+	/* NB. This can be a jffs2_xattr_datum or jffs2_xattr_ref and
+	   not actually a jffs2_inode_cache. Check ->class */
+	return ((struct jffs2_inode_cache *)raw);
+}
+
         /* flash_offset & 3 always has to be zero, because nodes are
         /* flash_offset & 3 always has to be zero, because nodes are
 	   always aligned at 4 bytes. So we have a couple of extra bits
 	   always aligned at 4 bytes. So we have a couple of extra bits
 	   to play with, which indicate the node's status; see below: */
 	   to play with, which indicate the node's status; see below: */
@@ -95,6 +133,11 @@ struct jffs2_raw_node_ref
 #define ref_obsolete(ref)	(((ref)->flash_offset & 3) == REF_OBSOLETE)
 #define ref_obsolete(ref)	(((ref)->flash_offset & 3) == REF_OBSOLETE)
 #define mark_ref_normal(ref)    do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
 #define mark_ref_normal(ref)    do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
 
 
+/* NB: REF_PRISTINE for an inode-less node (ref->next_in_ino == NULL) indicates
+   it is an unknown node of type JFFS2_NODETYPE_RWCOMPAT_COPY, so it'll get
+   copied. If you need to do anything different to GC inode-less nodes, then
+   you need to modify gc.c accordingly. */
+
 /* For each inode in the filesystem, we need to keep a record of
 /* For each inode in the filesystem, we need to keep a record of
    nlink, because it would be a PITA to scan the whole directory tree
    nlink, because it would be a PITA to scan the whole directory tree
    at read_inode() time to calculate it, and to keep sufficient information
    at read_inode() time to calculate it, and to keep sufficient information
@@ -103,15 +146,27 @@ struct jffs2_raw_node_ref
    a pointer to the first physical node which is part of this inode, too.
    a pointer to the first physical node which is part of this inode, too.
 */
 */
 struct jffs2_inode_cache {
 struct jffs2_inode_cache {
+	/* First part of structure is shared with other objects which
+	   can terminate the raw node refs' next_in_ino list -- which
+	   currently struct jffs2_xattr_datum and struct jffs2_xattr_ref. */
+
 	struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
 	struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
 		temporary lists of dirents, and later must be set to
 		temporary lists of dirents, and later must be set to
 		NULL to mark the end of the raw_node_ref->next_in_ino
 		NULL to mark the end of the raw_node_ref->next_in_ino
 		chain. */
 		chain. */
-	struct jffs2_inode_cache *next;
 	struct jffs2_raw_node_ref *nodes;
 	struct jffs2_raw_node_ref *nodes;
+	uint8_t class;	/* It's used for identification */
+
+	/* end of shared structure */
+
+	uint8_t flags;
+	uint16_t state;
 	uint32_t ino;
 	uint32_t ino;
+	struct jffs2_inode_cache *next;
+#ifdef CONFIG_JFFS2_FS_XATTR
+	struct jffs2_xattr_ref *xref;
+#endif
 	int nlink;
 	int nlink;
-	int state;
 };
 };
 
 
 /* Inode states for 'state' above. We need the 'GC' state to prevent
 /* Inode states for 'state' above. We need the 'GC' state to prevent
@@ -125,8 +180,16 @@ struct jffs2_inode_cache {
 #define INO_STATE_READING	5	/* In read_inode() */
 #define INO_STATE_READING	5	/* In read_inode() */
 #define INO_STATE_CLEARING	6	/* In clear_inode() */
 #define INO_STATE_CLEARING	6	/* In clear_inode() */
 
 
+#define INO_FLAGS_XATTR_CHECKED	0x01	/* has no duplicate xattr_ref */
+
+#define RAWNODE_CLASS_INODE_CACHE	0
+#define RAWNODE_CLASS_XATTR_DATUM	1
+#define RAWNODE_CLASS_XATTR_REF		2
+
 #define INOCACHE_HASHSIZE 128
 #define INOCACHE_HASHSIZE 128
 
 
+#define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size)
+
 /*
 /*
   Larger representation of a raw node, kept in-core only when the
   Larger representation of a raw node, kept in-core only when the
   struct inode for this particular ino is instantiated.
   struct inode for this particular ino is instantiated.
@@ -192,6 +255,7 @@ struct jffs2_eraseblock
 	uint32_t wasted_size;
 	uint32_t wasted_size;
 	uint32_t free_size;	/* Note that sector_size - free_size
 	uint32_t free_size;	/* Note that sector_size - free_size
 				   is the address of the first free space */
 				   is the address of the first free space */
+	uint32_t allocated_refs;
 	struct jffs2_raw_node_ref *first_node;
 	struct jffs2_raw_node_ref *first_node;
 	struct jffs2_raw_node_ref *last_node;
 	struct jffs2_raw_node_ref *last_node;
 
 
@@ -203,57 +267,7 @@ static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c)
 	return ((c->flash_size / c->sector_size) * sizeof (struct jffs2_eraseblock)) > (128 * 1024);
 	return ((c->flash_size / c->sector_size) * sizeof (struct jffs2_eraseblock)) > (128 * 1024);
 }
 }
 
 
-/* Calculate totlen from surrounding nodes or eraseblock */
-static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
-				    struct jffs2_eraseblock *jeb,
-				    struct jffs2_raw_node_ref *ref)
-{
-	uint32_t ref_end;
-
-	if (ref->next_phys)
-		ref_end = ref_offset(ref->next_phys);
-	else {
-		if (!jeb)
-			jeb = &c->blocks[ref->flash_offset / c->sector_size];
-
-		/* Last node in block. Use free_space */
-		BUG_ON(ref != jeb->last_node);
-		ref_end = jeb->offset + c->sector_size - jeb->free_size;
-	}
-	return ref_end - ref_offset(ref);
-}
-
-static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
-				  struct jffs2_eraseblock *jeb,
-				  struct jffs2_raw_node_ref *ref)
-{
-	uint32_t ret;
-
-#if CONFIG_JFFS2_FS_DEBUG > 0
-	if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
-		printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
-		       jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
-		BUG();
-	}
-#endif
-
-#if 1
-	ret = ref->__totlen;
-#else
-	/* This doesn't actually work yet */
-	ret = __ref_totlen(c, jeb, ref);
-	if (ret != ref->__totlen) {
-		printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
-		       ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
-		       ret, ref->__totlen);
-		if (!jeb)
-			jeb = &c->blocks[ref->flash_offset / c->sector_size];
-		jffs2_dbg_dump_node_refs_nolock(c, jeb);
-		BUG();
-	}
-#endif
-	return ret;
-}
+#define ref_totlen(a, b, c) __jffs2_ref_totlen((a), (b), (c))
 
 
 #define ALLOC_NORMAL	0	/* Normal allocation */
 #define ALLOC_NORMAL	0	/* Normal allocation */
 #define ALLOC_DELETION	1	/* Deletion node. Best to allow it */
 #define ALLOC_DELETION	1	/* Deletion node. Best to allow it */
@@ -268,13 +282,15 @@ static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
 
 
 #define PAD(x) (((x)+3)&~3)
 #define PAD(x) (((x)+3)&~3)
 
 
-static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
+static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev)
 {
 {
-	while(raw->next_in_ino) {
-		raw = raw->next_in_ino;
+	if (old_valid_dev(rdev)) {
+		jdev->old = cpu_to_je16(old_encode_dev(rdev));
+		return sizeof(jdev->old);
+	} else {
+		jdev->new = cpu_to_je32(new_encode_dev(rdev));
+		return sizeof(jdev->new);
 	}
 	}
-
-	return ((struct jffs2_inode_cache *)raw);
 }
 }
 
 
 static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
 static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
@@ -324,28 +340,44 @@ void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *t
 int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
 int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
 void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
 void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
 int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn);
 int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn);
+struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
+					       struct jffs2_eraseblock *jeb,
+					       uint32_t ofs, uint32_t len,
+					       struct jffs2_inode_cache *ic);
+extern uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c,
+				   struct jffs2_eraseblock *jeb,
+				   struct jffs2_raw_node_ref *ref);
 
 
 /* nodemgmt.c */
 /* nodemgmt.c */
 int jffs2_thread_should_wake(struct jffs2_sb_info *c);
 int jffs2_thread_should_wake(struct jffs2_sb_info *c);
-int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
+int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
 			uint32_t *len, int prio, uint32_t sumsize);
 			uint32_t *len, int prio, uint32_t sumsize);
-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
 			uint32_t *len, uint32_t sumsize);
 			uint32_t *len, uint32_t sumsize);
-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
+struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c, 
+						       uint32_t ofs, uint32_t len,
+						       struct jffs2_inode_cache *ic);
 void jffs2_complete_reservation(struct jffs2_sb_info *c);
 void jffs2_complete_reservation(struct jffs2_sb_info *c);
 void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
 void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
 
 
 /* write.c */
 /* write.c */
 int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
 int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
 
 
-struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
-struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
+struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+					   struct jffs2_raw_inode *ri, const unsigned char *data,
+					   uint32_t datalen, int alloc_mode);
+struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+					     struct jffs2_raw_dirent *rd, const unsigned char *name,
+					     uint32_t namelen, int alloc_mode);
 int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 			    struct jffs2_raw_inode *ri, unsigned char *buf,
 			    struct jffs2_raw_inode *ri, unsigned char *buf,
 			    uint32_t offset, uint32_t writelen, uint32_t *retlen);
 			    uint32_t offset, uint32_t writelen, uint32_t *retlen);
-int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
-int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f, uint32_t time);
-int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen, uint32_t time);
+int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f,
+		    struct jffs2_raw_inode *ri, const char *name, int namelen);
+int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name,
+		    int namelen, struct jffs2_inode_info *dead_f, uint32_t time);
+int jffs2_do_link(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino,
+		   uint8_t type, const char *name, int namelen, uint32_t time);
 
 
 
 
 /* readinode.c */
 /* readinode.c */
@@ -368,12 +400,19 @@ struct jffs2_raw_inode *jffs2_alloc_raw_inode(void);
 void jffs2_free_raw_inode(struct jffs2_raw_inode *);
 void jffs2_free_raw_inode(struct jffs2_raw_inode *);
 struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void);
 struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void);
 void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *);
 void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *);
-struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void);
-void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *);
+int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
+				 struct jffs2_eraseblock *jeb, int nr);
+void jffs2_free_refblock(struct jffs2_raw_node_ref *);
 struct jffs2_node_frag *jffs2_alloc_node_frag(void);
 struct jffs2_node_frag *jffs2_alloc_node_frag(void);
 void jffs2_free_node_frag(struct jffs2_node_frag *);
 void jffs2_free_node_frag(struct jffs2_node_frag *);
 struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
 struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
 void jffs2_free_inode_cache(struct jffs2_inode_cache *);
 void jffs2_free_inode_cache(struct jffs2_inode_cache *);
+#ifdef CONFIG_JFFS2_FS_XATTR
+struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void);
+void jffs2_free_xattr_datum(struct jffs2_xattr_datum *);
+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void);
+void jffs2_free_xattr_ref(struct jffs2_xattr_ref *);
+#endif
 
 
 /* gc.c */
 /* gc.c */
 int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
 int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
@@ -393,12 +432,14 @@ int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf,
 				uint32_t ofs, uint32_t len);
 				uint32_t ofs, uint32_t len);
 struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
 struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
 int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t size);
 
 
 /* build.c */
 /* build.c */
 int jffs2_do_mount_fs(struct jffs2_sb_info *c);
 int jffs2_do_mount_fs(struct jffs2_sb_info *c);
 
 
 /* erase.c */
 /* erase.c */
 void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
 void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
+void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 
 
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 /* wbuf.c */
 /* wbuf.c */

+ 84 - 112
fs/jffs2/nodemgmt.c

@@ -23,13 +23,12 @@
  *	jffs2_reserve_space - request physical space to write nodes to flash
  *	jffs2_reserve_space - request physical space to write nodes to flash
  *	@c: superblock info
  *	@c: superblock info
  *	@minsize: Minimum acceptable size of allocation
  *	@minsize: Minimum acceptable size of allocation
- *	@ofs: Returned value of node offset
  *	@len: Returned value of allocation length
  *	@len: Returned value of allocation length
  *	@prio: Allocation type - ALLOC_{NORMAL,DELETION}
  *	@prio: Allocation type - ALLOC_{NORMAL,DELETION}
  *
  *
  *	Requests a block of physical space on the flash. Returns zero for success
  *	Requests a block of physical space on the flash. Returns zero for success
- *	and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC
- *	or other error if appropriate.
+ *	and puts 'len' into the appropriate place, or returns -ENOSPC or other 
+ *	error if appropriate. Doesn't return len since that's 
  *
  *
  *	If it returns zero, jffs2_reserve_space() also downs the per-filesystem
  *	If it returns zero, jffs2_reserve_space() also downs the per-filesystem
  *	allocation semaphore, to prevent more than one allocation from being
  *	allocation semaphore, to prevent more than one allocation from being
@@ -40,9 +39,9 @@
  */
  */
 
 
 static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize,
 static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize,
-					uint32_t *ofs, uint32_t *len, uint32_t sumsize);
+				  uint32_t *len, uint32_t sumsize);
 
 
-int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
+int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
 			uint32_t *len, int prio, uint32_t sumsize)
 			uint32_t *len, int prio, uint32_t sumsize)
 {
 {
 	int ret = -EAGAIN;
 	int ret = -EAGAIN;
@@ -132,19 +131,21 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
 			spin_lock(&c->erase_completion_lock);
 			spin_lock(&c->erase_completion_lock);
 		}
 		}
 
 
-		ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize);
+		ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
 		if (ret) {
 		if (ret) {
 			D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
 			D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
 		}
 		}
 	}
 	}
 	spin_unlock(&c->erase_completion_lock);
 	spin_unlock(&c->erase_completion_lock);
+	if (!ret)
+		ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
 	if (ret)
 	if (ret)
 		up(&c->alloc_sem);
 		up(&c->alloc_sem);
 	return ret;
 	return ret;
 }
 }
 
 
-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
-			uint32_t *len, uint32_t sumsize)
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
+			   uint32_t *len, uint32_t sumsize)
 {
 {
 	int ret = -EAGAIN;
 	int ret = -EAGAIN;
 	minsize = PAD(minsize);
 	minsize = PAD(minsize);
@@ -153,12 +154,15 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *
 
 
 	spin_lock(&c->erase_completion_lock);
 	spin_lock(&c->erase_completion_lock);
 	while(ret == -EAGAIN) {
 	while(ret == -EAGAIN) {
-		ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize);
+		ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
 		if (ret) {
 		if (ret) {
 		        D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
 		        D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
 		}
 		}
 	}
 	}
 	spin_unlock(&c->erase_completion_lock);
 	spin_unlock(&c->erase_completion_lock);
+	if (!ret)
+		ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
+
 	return ret;
 	return ret;
 }
 }
 
 
@@ -259,10 +263,11 @@ static int jffs2_find_nextblock(struct jffs2_sb_info *c)
 }
 }
 
 
 /* Called with alloc sem _and_ erase_completion_lock */
 /* Called with alloc sem _and_ erase_completion_lock */
-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize)
+static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
+				  uint32_t *len, uint32_t sumsize)
 {
 {
 	struct jffs2_eraseblock *jeb = c->nextblock;
 	struct jffs2_eraseblock *jeb = c->nextblock;
-	uint32_t reserved_size; 			/* for summary information at the end of the jeb */
+	uint32_t reserved_size;				/* for summary information at the end of the jeb */
 	int ret;
 	int ret;
 
 
  restart:
  restart:
@@ -312,6 +317,8 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
 		}
 		}
 	} else {
 	} else {
 		if (jeb && minsize > jeb->free_size) {
 		if (jeb && minsize > jeb->free_size) {
+			uint32_t waste;
+
 			/* Skip the end of this block and file it as having some dirty space */
 			/* Skip the end of this block and file it as having some dirty space */
 			/* If there's a pending write to it, flush now */
 			/* If there's a pending write to it, flush now */
 
 
@@ -324,10 +331,26 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
 				goto restart;
 				goto restart;
 			}
 			}
 
 
-			c->wasted_size += jeb->free_size;
-			c->free_size -= jeb->free_size;
-			jeb->wasted_size += jeb->free_size;
-			jeb->free_size = 0;
+			spin_unlock(&c->erase_completion_lock);
+
+			ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
+			if (ret)
+				return ret;
+			/* Just lock it again and continue. Nothing much can change because
+			   we hold c->alloc_sem anyway. In fact, it's not entirely clear why
+			   we hold c->erase_completion_lock in the majority of this function...
+			   but that's a question for another (more caffeine-rich) day. */
+			spin_lock(&c->erase_completion_lock);
+
+			waste = jeb->free_size;
+			jffs2_link_node_ref(c, jeb,
+					    (jeb->offset + c->sector_size - waste) | REF_OBSOLETE,
+					    waste, NULL);
+			/* FIXME: that made it count as dirty. Convert to wasted */
+			jeb->dirty_size -= waste;
+			c->dirty_size -= waste;
+			jeb->wasted_size += waste;
+			c->wasted_size += waste;
 
 
 			jffs2_close_nextblock(c, jeb);
 			jffs2_close_nextblock(c, jeb);
 			jeb = NULL;
 			jeb = NULL;
@@ -349,7 +372,6 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
 	}
 	}
 	/* OK, jeb (==c->nextblock) is now pointing at a block which definitely has
 	/* OK, jeb (==c->nextblock) is now pointing at a block which definitely has
 	   enough space */
 	   enough space */
-	*ofs = jeb->offset + (c->sector_size - jeb->free_size);
 	*len = jeb->free_size - reserved_size;
 	*len = jeb->free_size - reserved_size;
 
 
 	if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
 	if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
@@ -365,7 +387,8 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
 		spin_lock(&c->erase_completion_lock);
 		spin_lock(&c->erase_completion_lock);
 	}
 	}
 
 
-	D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
+	D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n",
+		  *len, jeb->offset + (c->sector_size - jeb->free_size)));
 	return 0;
 	return 0;
 }
 }
 
 
@@ -374,7 +397,6 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
  *	@c: superblock info
  *	@c: superblock info
  *	@new: new node reference to add
  *	@new: new node reference to add
  *	@len: length of this physical node
  *	@len: length of this physical node
- *	@dirty: dirty flag for new node
  *
  *
  *	Should only be used to report nodes for which space has been allocated
  *	Should only be used to report nodes for which space has been allocated
  *	by jffs2_reserve_space.
  *	by jffs2_reserve_space.
@@ -382,42 +404,30 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
  *	Must be called with the alloc_sem held.
  *	Must be called with the alloc_sem held.
  */
  */
 
 
-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
+struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
+						       uint32_t ofs, uint32_t len,
+						       struct jffs2_inode_cache *ic)
 {
 {
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_eraseblock *jeb;
-	uint32_t len;
+	struct jffs2_raw_node_ref *new;
 
 
-	jeb = &c->blocks[new->flash_offset / c->sector_size];
-	len = ref_totlen(c, jeb, new);
+	jeb = &c->blocks[ofs / c->sector_size];
 
 
-	D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
+	D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n",
+		  ofs & ~3, ofs & 3, len));
 #if 1
 #if 1
-	/* we could get some obsolete nodes after nextblock was refiled
-	   in wbuf.c */
-	if ((c->nextblock || !ref_obsolete(new))
-	    &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) {
+	/* Allow non-obsolete nodes only to be added at the end of c->nextblock, 
+	   if c->nextblock is set. Note that wbuf.c will file obsolete nodes
+	   even after refiling c->nextblock */
+	if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
+	    && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) {
 		printk(KERN_WARNING "argh. node added in wrong place\n");
 		printk(KERN_WARNING "argh. node added in wrong place\n");
-		jffs2_free_raw_node_ref(new);
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 	}
 	}
 #endif
 #endif
 	spin_lock(&c->erase_completion_lock);
 	spin_lock(&c->erase_completion_lock);
 
 
-	if (!jeb->first_node)
-		jeb->first_node = new;
-	if (jeb->last_node)
-		jeb->last_node->next_phys = new;
-	jeb->last_node = new;
-
-	jeb->free_size -= len;
-	c->free_size -= len;
-	if (ref_obsolete(new)) {
-		jeb->dirty_size += len;
-		c->dirty_size += len;
-	} else {
-		jeb->used_size += len;
-		c->used_size += len;
-	}
+	new = jffs2_link_node_ref(c, jeb, ofs, len, ic);
 
 
 	if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
 	if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
 		/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
 		/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
@@ -438,7 +448,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
 
 
 	spin_unlock(&c->erase_completion_lock);
 	spin_unlock(&c->erase_completion_lock);
 
 
-	return 0;
+	return new;
 }
 }
 
 
 
 
@@ -470,8 +480,9 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 	struct jffs2_unknown_node n;
 	struct jffs2_unknown_node n;
 	int ret, addedsize;
 	int ret, addedsize;
 	size_t retlen;
 	size_t retlen;
+	uint32_t freed_len;
 
 
-	if(!ref) {
+	if(unlikely(!ref)) {
 		printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
 		printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
 		return;
 		return;
 	}
 	}
@@ -499,32 +510,34 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 
 
 	spin_lock(&c->erase_completion_lock);
 	spin_lock(&c->erase_completion_lock);
 
 
+	freed_len = ref_totlen(c, jeb, ref);
+
 	if (ref_flags(ref) == REF_UNCHECKED) {
 	if (ref_flags(ref) == REF_UNCHECKED) {
-		D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
+		D1(if (unlikely(jeb->unchecked_size < freed_len)) {
 			printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
 			printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
-			       ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+			       freed_len, blocknr, ref->flash_offset, jeb->used_size);
 			BUG();
 			BUG();
 		})
 		})
-		D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
-		jeb->unchecked_size -= ref_totlen(c, jeb, ref);
-		c->unchecked_size -= ref_totlen(c, jeb, ref);
+		D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), freed_len));
+		jeb->unchecked_size -= freed_len;
+		c->unchecked_size -= freed_len;
 	} else {
 	} else {
-		D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
+		D1(if (unlikely(jeb->used_size < freed_len)) {
 			printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
 			printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
-			       ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+			       freed_len, blocknr, ref->flash_offset, jeb->used_size);
 			BUG();
 			BUG();
 		})
 		})
-		D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
-		jeb->used_size -= ref_totlen(c, jeb, ref);
-		c->used_size -= ref_totlen(c, jeb, ref);
+		D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), freed_len));
+		jeb->used_size -= freed_len;
+		c->used_size -= freed_len;
 	}
 	}
 
 
 	// Take care, that wasted size is taken into concern
 	// Take care, that wasted size is taken into concern
-	if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
-		D1(printk(KERN_DEBUG "Dirtying\n"));
-		addedsize = ref_totlen(c, jeb, ref);
-		jeb->dirty_size += ref_totlen(c, jeb, ref);
-		c->dirty_size += ref_totlen(c, jeb, ref);
+	if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + freed_len)) && jeb != c->nextblock) {
+		D1(printk("Dirtying\n"));
+		addedsize = freed_len;
+		jeb->dirty_size += freed_len;
+		c->dirty_size += freed_len;
 
 
 		/* Convert wasted space to dirty, if not a bad block */
 		/* Convert wasted space to dirty, if not a bad block */
 		if (jeb->wasted_size) {
 		if (jeb->wasted_size) {
@@ -543,10 +556,10 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 			}
 			}
 		}
 		}
 	} else {
 	} else {
-		D1(printk(KERN_DEBUG "Wasting\n"));
+		D1(printk("Wasting\n"));
 		addedsize = 0;
 		addedsize = 0;
-		jeb->wasted_size += ref_totlen(c, jeb, ref);
-		c->wasted_size += ref_totlen(c, jeb, ref);
+		jeb->wasted_size += freed_len;
+		c->wasted_size += freed_len;
 	}
 	}
 	ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
 	ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
 
 
@@ -622,7 +635,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 	/* The erase_free_sem is locked, and has been since before we marked the node obsolete
 	/* The erase_free_sem is locked, and has been since before we marked the node obsolete
 	   and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
 	   and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
 	   the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
 	   the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
-	   by jffs2_free_all_node_refs() in erase.c. Which is nice. */
+	   by jffs2_free_jeb_node_refs() in erase.c. Which is nice. */
 
 
 	D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
 	D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
 	ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
 	ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
@@ -634,8 +647,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 		printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
 		printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
 		goto out_erase_sem;
 		goto out_erase_sem;
 	}
 	}
-	if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
-		printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
+	if (PAD(je32_to_cpu(n.totlen)) != PAD(freed_len)) {
+		printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), freed_len);
 		goto out_erase_sem;
 		goto out_erase_sem;
 	}
 	}
 	if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
 	if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
@@ -671,6 +684,10 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 		spin_lock(&c->erase_completion_lock);
 		spin_lock(&c->erase_completion_lock);
 
 
 		ic = jffs2_raw_ref_to_ic(ref);
 		ic = jffs2_raw_ref_to_ic(ref);
+		/* It seems we should never call jffs2_mark_node_obsolete() for
+		   XATTR nodes.... yet. Make sure we notice if/when we change
+		   that :) */
+		BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE);
 		for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
 		for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
 			;
 			;
 
 
@@ -683,51 +700,6 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 		spin_unlock(&c->erase_completion_lock);
 		spin_unlock(&c->erase_completion_lock);
 	}
 	}
 
 
-
-	/* Merge with the next node in the physical list, if there is one
-	   and if it's also obsolete and if it doesn't belong to any inode */
-	if (ref->next_phys && ref_obsolete(ref->next_phys) &&
-	    !ref->next_phys->next_in_ino) {
-		struct jffs2_raw_node_ref *n = ref->next_phys;
-
-		spin_lock(&c->erase_completion_lock);
-
-		ref->__totlen += n->__totlen;
-		ref->next_phys = n->next_phys;
-                if (jeb->last_node == n) jeb->last_node = ref;
-		if (jeb->gc_node == n) {
-			/* gc will be happy continuing gc on this node */
-			jeb->gc_node=ref;
-		}
-		spin_unlock(&c->erase_completion_lock);
-
-		jffs2_free_raw_node_ref(n);
-	}
-
-	/* Also merge with the previous node in the list, if there is one
-	   and that one is obsolete */
-	if (ref != jeb->first_node ) {
-		struct jffs2_raw_node_ref *p = jeb->first_node;
-
-		spin_lock(&c->erase_completion_lock);
-
-		while (p->next_phys != ref)
-			p = p->next_phys;
-
-		if (ref_obsolete(p) && !ref->next_in_ino) {
-			p->__totlen += ref->__totlen;
-			if (jeb->last_node == ref) {
-				jeb->last_node = p;
-			}
-			if (jeb->gc_node == ref) {
-				/* gc will be happy continuing gc on this node */
-				jeb->gc_node=p;
-			}
-			p->next_phys = ref->next_phys;
-			jffs2_free_raw_node_ref(ref);
-		}
-		spin_unlock(&c->erase_completion_lock);
-	}
  out_erase_sem:
  out_erase_sem:
 	up(&c->erase_free_sem);
 	up(&c->erase_free_sem);
 }
 }

+ 8 - 15
fs/jffs2/os-linux.h

@@ -31,9 +31,7 @@ struct kvec;
 #define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
 #define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
 #define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
 #define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
 #define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
 #define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
-
-#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
-#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
+#define JFFS2_F_I_RDEV(f) (OFNI_EDONI_2SFFJ(f)->i_rdev)
 
 
 #define ITIME(sec) ((struct timespec){sec, 0})
 #define ITIME(sec) ((struct timespec){sec, 0})
 #define I_SEC(tv) ((tv).tv_sec)
 #define I_SEC(tv) ((tv).tv_sec)
@@ -60,6 +58,10 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
 	f->target = NULL;
 	f->target = NULL;
 	f->flags = 0;
 	f->flags = 0;
 	f->usercompr = 0;
 	f->usercompr = 0;
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+	f->i_acl_access = JFFS2_ACL_NOT_CACHED;
+	f->i_acl_default = JFFS2_ACL_NOT_CACHED;
+#endif
 }
 }
 
 
 
 
@@ -90,13 +92,10 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
 #define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
 #define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
 #define jffs2_wbuf_timeout NULL
 #define jffs2_wbuf_timeout NULL
 #define jffs2_wbuf_process NULL
 #define jffs2_wbuf_process NULL
-#define jffs2_nor_ecc(c) (0)
 #define jffs2_dataflash(c) (0)
 #define jffs2_dataflash(c) (0)
-#define jffs2_nor_wbuf_flash(c) (0)
-#define jffs2_nor_ecc_flash_setup(c) (0)
-#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
 #define jffs2_dataflash_setup(c) (0)
 #define jffs2_dataflash_setup(c) (0)
 #define jffs2_dataflash_cleanup(c) do {} while (0)
 #define jffs2_dataflash_cleanup(c) do {} while (0)
+#define jffs2_nor_wbuf_flash(c) (0)
 #define jffs2_nor_wbuf_flash_setup(c) (0)
 #define jffs2_nor_wbuf_flash_setup(c) (0)
 #define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0)
 #define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0)
 
 
@@ -107,9 +106,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
 #ifdef CONFIG_JFFS2_SUMMARY
 #ifdef CONFIG_JFFS2_SUMMARY
 #define jffs2_can_mark_obsolete(c) (0)
 #define jffs2_can_mark_obsolete(c) (0)
 #else
 #else
-#define jffs2_can_mark_obsolete(c) \
-  ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & (MTD_ECC|MTD_PROGRAM_REGIONS))) || \
-   c->mtd->type == MTD_RAM)
+#define jffs2_can_mark_obsolete(c) (c->mtd->flags & (MTD_BIT_WRITEABLE))
 #endif
 #endif
 
 
 #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
 #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
@@ -133,15 +130,11 @@ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
 int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
 int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
 void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
 void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
 
 
-#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
-int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
-void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
-
 #define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
 #define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
 int jffs2_dataflash_setup(struct jffs2_sb_info *c);
 int jffs2_dataflash_setup(struct jffs2_sb_info *c);
 void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
 void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
 
 
-#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_PROGRAM_REGIONS))
+#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE))
 int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c);
 int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c);
 void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c);
 void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c);
 
 

+ 74 - 45
fs/jffs2/readinode.c

@@ -116,19 +116,42 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r
 				uint32_t *latest_mctime, uint32_t *mctime_ver)
 				uint32_t *latest_mctime, uint32_t *mctime_ver)
 {
 {
 	struct jffs2_full_dirent *fd;
 	struct jffs2_full_dirent *fd;
+	uint32_t crc;
 
 
-	/* The direntry nodes are checked during the flash scanning */
-	BUG_ON(ref_flags(ref) == REF_UNCHECKED);
 	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
 	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
 	BUG_ON(ref_obsolete(ref));
 	BUG_ON(ref_obsolete(ref));
 
 
-	/* Sanity check */
-	if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
-		JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
-		       ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
+	crc = crc32(0, rd, sizeof(*rd) - 8);
+	if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
+		JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n",
+			     ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
 		return 1;
 		return 1;
 	}
 	}
 
 
+	/* If we've never checked the CRCs on this node, check them now */
+	if (ref_flags(ref) == REF_UNCHECKED) {
+		struct jffs2_eraseblock *jeb;
+		int len;
+
+		/* Sanity check */
+		if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
+			JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
+				    ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
+			return 1;
+		}
+
+		jeb = &c->blocks[ref->flash_offset / c->sector_size];
+		len = ref_totlen(c, jeb, ref);
+
+		spin_lock(&c->erase_completion_lock);
+		jeb->used_size += len;
+		jeb->unchecked_size -= len;
+		c->used_size += len;
+		c->unchecked_size -= len;
+		ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
+		spin_unlock(&c->erase_completion_lock);
+	}
+
 	fd = jffs2_alloc_full_dirent(rd->nsize + 1);
 	fd = jffs2_alloc_full_dirent(rd->nsize + 1);
 	if (unlikely(!fd))
 	if (unlikely(!fd))
 		return -ENOMEM;
 		return -ENOMEM;
@@ -198,13 +221,21 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 	struct jffs2_tmp_dnode_info *tn;
 	struct jffs2_tmp_dnode_info *tn;
 	uint32_t len, csize;
 	uint32_t len, csize;
 	int ret = 1;
 	int ret = 1;
+	uint32_t crc;
 
 
 	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
 	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
 	BUG_ON(ref_obsolete(ref));
 	BUG_ON(ref_obsolete(ref));
 
 
+	crc = crc32(0, rd, sizeof(*rd) - 8);
+	if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
+		JFFS2_NOTICE("node CRC failed on dnode at %#08x: read %#08x, calculated %#08x\n",
+			     ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
+		return 1;
+	}
+
 	tn = jffs2_alloc_tmp_dnode_info();
 	tn = jffs2_alloc_tmp_dnode_info();
 	if (!tn) {
 	if (!tn) {
-		JFFS2_ERROR("failed to allocate tn (%d bytes).\n", sizeof(*tn));
+		JFFS2_ERROR("failed to allocate tn (%zu bytes).\n", sizeof(*tn));
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
@@ -213,14 +244,6 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 
 
 	/* If we've never checked the CRCs on this node, check them now */
 	/* If we've never checked the CRCs on this node, check them now */
 	if (ref_flags(ref) == REF_UNCHECKED) {
 	if (ref_flags(ref) == REF_UNCHECKED) {
-		uint32_t crc;
-
-		crc = crc32(0, rd, sizeof(*rd) - 8);
-		if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
-			JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
-					ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
-			goto free_out;
-		}
 
 
 		/* Sanity checks */
 		/* Sanity checks */
 		if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) ||
 		if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) ||
@@ -343,7 +366,7 @@ free_out:
  * Helper function for jffs2_get_inode_nodes().
  * Helper function for jffs2_get_inode_nodes().
  * It is called every time an unknown node is found.
  * It is called every time an unknown node is found.
  *
  *
- * Returns: 0 on succes;
+ * Returns: 0 on success;
  * 	    1 if the node should be marked obsolete;
  * 	    1 if the node should be marked obsolete;
  * 	    negative error code on failure.
  * 	    negative error code on failure.
  */
  */
@@ -354,37 +377,30 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re
 
 
 	un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype));
 	un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype));
 
 
-	if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) {
-		/* Hmmm. This should have been caught at scan time. */
-		JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref));
-		jffs2_dbg_dump_node(c, ref_offset(ref));
-		return 1;
-	} else {
-		switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
+	switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
 
 
-		case JFFS2_FEATURE_INCOMPAT:
-			JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n",
-				je16_to_cpu(un->nodetype), ref_offset(ref));
-			/* EEP */
-			BUG();
-			break;
+	case JFFS2_FEATURE_INCOMPAT:
+		JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n",
+			    je16_to_cpu(un->nodetype), ref_offset(ref));
+		/* EEP */
+		BUG();
+		break;
 
 
-		case JFFS2_FEATURE_ROCOMPAT:
-			JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n",
-					je16_to_cpu(un->nodetype), ref_offset(ref));
-			BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO));
-			break;
+	case JFFS2_FEATURE_ROCOMPAT:
+		JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n",
+			    je16_to_cpu(un->nodetype), ref_offset(ref));
+		BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO));
+		break;
 
 
-		case JFFS2_FEATURE_RWCOMPAT_COPY:
-			JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
-					je16_to_cpu(un->nodetype), ref_offset(ref));
-			break;
+	case JFFS2_FEATURE_RWCOMPAT_COPY:
+		JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
+			     je16_to_cpu(un->nodetype), ref_offset(ref));
+		break;
 
 
-		case JFFS2_FEATURE_RWCOMPAT_DELETE:
-			JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
-					je16_to_cpu(un->nodetype), ref_offset(ref));
-			return 1;
-		}
+	case JFFS2_FEATURE_RWCOMPAT_DELETE:
+		JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
+			     je16_to_cpu(un->nodetype), ref_offset(ref));
+		return 1;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -434,7 +450,7 @@ static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
 	}
 	}
 
 
 	if (retlen < len) {
 	if (retlen < len) {
-		JFFS2_ERROR("short read at %#08x: %d instead of %d.\n",
+		JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n",
 				offs, retlen, len);
 				offs, retlen, len);
 		return -EIO;
 		return -EIO;
 	}
 	}
@@ -542,13 +558,25 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
 		}
 		}
 
 
 		if (retlen < len) {
 		if (retlen < len) {
-			JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", ref_offset(ref), retlen, len);
+			JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n", ref_offset(ref), retlen, len);
 			err = -EIO;
 			err = -EIO;
 			goto free_out;
 			goto free_out;
 		}
 		}
 
 
 		node = (union jffs2_node_union *)bufstart;
 		node = (union jffs2_node_union *)bufstart;
 
 
+		/* No need to mask in the valid bit; it shouldn't be invalid */
+		if (je32_to_cpu(node->u.hdr_crc) != crc32(0, node, sizeof(node->u)-4)) {
+			JFFS2_NOTICE("Node header CRC failed at %#08x. {%04x,%04x,%08x,%08x}\n",
+				     ref_offset(ref), je16_to_cpu(node->u.magic),
+				     je16_to_cpu(node->u.nodetype),
+				     je32_to_cpu(node->u.totlen),
+				     je32_to_cpu(node->u.hdr_crc));
+			jffs2_dbg_dump_node(c, ref_offset(ref));
+			jffs2_mark_node_obsolete(c, ref);
+			goto cont;
+		}
+
 		switch (je16_to_cpu(node->u.nodetype)) {
 		switch (je16_to_cpu(node->u.nodetype)) {
 
 
 		case JFFS2_NODETYPE_DIRENT:
 		case JFFS2_NODETYPE_DIRENT:
@@ -606,6 +634,7 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
 				goto free_out;
 				goto free_out;
 
 
 		}
 		}
+	cont:
 		spin_lock(&c->erase_completion_lock);
 		spin_lock(&c->erase_completion_lock);
 	}
 	}
 
 

+ 302 - 140
fs/jffs2/scan.c

@@ -65,6 +65,28 @@ static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
 		return DEFAULT_EMPTY_SCAN_SIZE;
 		return DEFAULT_EMPTY_SCAN_SIZE;
 }
 }
 
 
+static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	int ret;
+
+	if ((ret = jffs2_prealloc_raw_node_refs(c, jeb, 1)))
+		return ret;
+	if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size)))
+		return ret;
+	/* Turned wasted size into dirty, since we apparently 
+	   think it's recoverable now. */
+	jeb->dirty_size += jeb->wasted_size;
+	c->dirty_size += jeb->wasted_size;
+	c->wasted_size -= jeb->wasted_size;
+	jeb->wasted_size = 0;
+	if (VERYDIRTY(c, jeb->dirty_size)) {
+		list_add(&jeb->list, &c->very_dirty_list);
+	} else {
+		list_add(&jeb->list, &c->dirty_list);
+	}
+	return 0;
+}
+
 int jffs2_scan_medium(struct jffs2_sb_info *c)
 int jffs2_scan_medium(struct jffs2_sb_info *c)
 {
 {
 	int i, ret;
 	int i, ret;
@@ -170,34 +192,20 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
 					(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
 					(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
 				/* Better candidate for the next writes to go to */
 				/* Better candidate for the next writes to go to */
 				if (c->nextblock) {
 				if (c->nextblock) {
-					c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
-					c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
-					c->free_size -= c->nextblock->free_size;
-					c->wasted_size -= c->nextblock->wasted_size;
-					c->nextblock->free_size = c->nextblock->wasted_size = 0;
-					if (VERYDIRTY(c, c->nextblock->dirty_size)) {
-						list_add(&c->nextblock->list, &c->very_dirty_list);
-					} else {
-						list_add(&c->nextblock->list, &c->dirty_list);
-					}
+					ret = file_dirty(c, c->nextblock);
+					if (ret)
+						return ret;
 					/* deleting summary information of the old nextblock */
 					/* deleting summary information of the old nextblock */
 					jffs2_sum_reset_collected(c->summary);
 					jffs2_sum_reset_collected(c->summary);
 				}
 				}
-				/* update collected summary infromation for the current nextblock */
+				/* update collected summary information for the current nextblock */
 				jffs2_sum_move_collected(c, s);
 				jffs2_sum_move_collected(c, s);
 				D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset));
 				D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset));
 				c->nextblock = jeb;
 				c->nextblock = jeb;
 			} else {
 			} else {
-				jeb->dirty_size += jeb->free_size + jeb->wasted_size;
-				c->dirty_size += jeb->free_size + jeb->wasted_size;
-				c->free_size -= jeb->free_size;
-				c->wasted_size -= jeb->wasted_size;
-				jeb->free_size = jeb->wasted_size = 0;
-				if (VERYDIRTY(c, jeb->dirty_size)) {
-					list_add(&jeb->list, &c->very_dirty_list);
-				} else {
-					list_add(&jeb->list, &c->dirty_list);
-				}
+				ret = file_dirty(c, jeb);
+				if (ret)
+					return ret;
 			}
 			}
 			break;
 			break;
 
 
@@ -222,9 +230,6 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
 		}
 		}
 	}
 	}
 
 
-	if (jffs2_sum_active() && s)
-		kfree(s);
-
 	/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
 	/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
 	if (c->nextblock && (c->nextblock->dirty_size)) {
 	if (c->nextblock && (c->nextblock->dirty_size)) {
 		c->nextblock->wasted_size += c->nextblock->dirty_size;
 		c->nextblock->wasted_size += c->nextblock->dirty_size;
@@ -242,11 +247,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
 
 
 		D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
 		D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
 			  skip));
 			  skip));
-		c->nextblock->wasted_size += skip;
-		c->wasted_size += skip;
-
-		c->nextblock->free_size -= skip;
-		c->free_size -= skip;
+		jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
+		jffs2_scan_dirty_space(c, c->nextblock, skip);
 	}
 	}
 #endif
 #endif
 	if (c->nr_erasing_blocks) {
 	if (c->nr_erasing_blocks) {
@@ -266,6 +268,9 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
 	else
 	else
 		c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
 		c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
 #endif
 #endif
+	if (s)
+		kfree(s);
+
 	return ret;
 	return ret;
 }
 }
 
 
@@ -290,7 +295,7 @@ int jffs2_fill_scan_buf (struct jffs2_sb_info *c, void *buf,
 int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 {
 	if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
 	if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
-		&& (!jeb->first_node || !jeb->first_node->next_phys) )
+	    && (!jeb->first_node || !ref_next(jeb->first_node)) )
 		return BLK_STATE_CLEANMARKER;
 		return BLK_STATE_CLEANMARKER;
 
 
 	/* move blocks with max 4 byte dirty space to cleanlist */
 	/* move blocks with max 4 byte dirty space to cleanlist */
@@ -306,11 +311,119 @@ int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
 		return BLK_STATE_ALLDIRTY;
 		return BLK_STATE_ALLDIRTY;
 }
 }
 
 
+#ifdef CONFIG_JFFS2_FS_XATTR
+static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				 struct jffs2_raw_xattr *rx, uint32_t ofs,
+				 struct jffs2_summary *s)
+{
+	struct jffs2_xattr_datum *xd;
+	uint32_t totlen, crc;
+	int err;
+
+	crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4);
+	if (crc != je32_to_cpu(rx->node_crc)) {
+		if (je32_to_cpu(rx->node_crc) != 0xffffffff)
+			JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+				      ofs, je32_to_cpu(rx->node_crc), crc);
+		if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen))))
+			return err;
+		return 0;
+	}
+
+	totlen = PAD(sizeof(*rx) + rx->name_len + 1 + je16_to_cpu(rx->value_len));
+	if (totlen != je32_to_cpu(rx->totlen)) {
+		JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
+			      ofs, je32_to_cpu(rx->totlen), totlen);
+		if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen))))
+			return err;
+		return 0;
+	}
+
+	xd = jffs2_setup_xattr_datum(c, je32_to_cpu(rx->xid), je32_to_cpu(rx->version));
+	if (IS_ERR(xd)) {
+		if (PTR_ERR(xd) == -EEXIST) {
+			if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rx->totlen)))))
+				return err;
+			return 0;
+		}
+		return PTR_ERR(xd);
+	}
+	xd->xprefix = rx->xprefix;
+	xd->name_len = rx->name_len;
+	xd->value_len = je16_to_cpu(rx->value_len);
+	xd->data_crc = je32_to_cpu(rx->data_crc);
+
+	xd->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL);
+	/* FIXME */ xd->node->next_in_ino = (void *)xd;
+
+	if (jffs2_sum_active())
+		jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset);
+	dbg_xattr("scaning xdatum at %#08x (xid=%u, version=%u)\n",
+		  ofs, xd->xid, xd->version);
+	return 0;
+}
+
+static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				struct jffs2_raw_xref *rr, uint32_t ofs,
+				struct jffs2_summary *s)
+{
+	struct jffs2_xattr_ref *ref;
+	uint32_t crc;
+	int err;
+
+	crc = crc32(0, rr, sizeof(*rr) - 4);
+	if (crc != je32_to_cpu(rr->node_crc)) {
+		if (je32_to_cpu(rr->node_crc) != 0xffffffff)
+			JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+				      ofs, je32_to_cpu(rr->node_crc), crc);
+		if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rr->totlen)))))
+			return err;
+		return 0;
+	}
+
+	if (PAD(sizeof(struct jffs2_raw_xref)) != je32_to_cpu(rr->totlen)) {
+		JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%zd\n",
+			      ofs, je32_to_cpu(rr->totlen),
+			      PAD(sizeof(struct jffs2_raw_xref)));
+		if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rr->totlen))))
+			return err;
+		return 0;
+	}
+
+	ref = jffs2_alloc_xattr_ref();
+	if (!ref)
+		return -ENOMEM;
+
+	/* BEFORE jffs2_build_xattr_subsystem() called, 
+	 * ref->xid is used to store 32bit xid, xd is not used
+	 * ref->ino is used to store 32bit inode-number, ic is not used
+	 * Thoes variables are declared as union, thus using those
+	 * are exclusive. In a similar way, ref->next is temporarily
+	 * used to chain all xattr_ref object. It's re-chained to
+	 * jffs2_inode_cache in jffs2_build_xattr_subsystem() correctly.
+	 */
+	ref->ino = je32_to_cpu(rr->ino);
+	ref->xid = je32_to_cpu(rr->xid);
+	ref->next = c->xref_temp;
+	c->xref_temp = ref;
+
+	ref->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), NULL);
+	/* FIXME */ ref->node->next_in_ino = (void *)ref;
+
+	if (jffs2_sum_active())
+		jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset);
+	dbg_xattr("scan xref at %#08x (xid=%u, ino=%u)\n",
+		  ofs, ref->xid, ref->ino);
+	return 0;
+}
+#endif
+
+/* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into
+   the flash, XIP-style */
 static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-				unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
+				  unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
 	struct jffs2_unknown_node *node;
 	struct jffs2_unknown_node *node;
 	struct jffs2_unknown_node crcnode;
 	struct jffs2_unknown_node crcnode;
-	struct jffs2_sum_marker *sm;
 	uint32_t ofs, prevofs;
 	uint32_t ofs, prevofs;
 	uint32_t hdr_crc, buf_ofs, buf_len;
 	uint32_t hdr_crc, buf_ofs, buf_len;
 	int err;
 	int err;
@@ -344,44 +457,75 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
 #endif
 #endif
 
 
 	if (jffs2_sum_active()) {
 	if (jffs2_sum_active()) {
-		sm = kmalloc(sizeof(struct jffs2_sum_marker), GFP_KERNEL);
-		if (!sm) {
-			return -ENOMEM;
-		}
-
-		err = jffs2_fill_scan_buf(c, (unsigned char *) sm, jeb->offset + c->sector_size -
-					sizeof(struct jffs2_sum_marker), sizeof(struct jffs2_sum_marker));
-		if (err) {
-			kfree(sm);
-			return err;
-		}
-
-		if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC ) {
-			err = jffs2_sum_scan_sumnode(c, jeb, je32_to_cpu(sm->offset), &pseudo_random);
-			if (err) {
-				kfree(sm);
+		struct jffs2_sum_marker *sm;
+		void *sumptr = NULL;
+		uint32_t sumlen;
+	      
+		if (!buf_size) {
+			/* XIP case. Just look, point at the summary if it's there */
+			sm = (void *)buf + c->sector_size - sizeof(*sm);
+			if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) {
+				sumptr = buf + je32_to_cpu(sm->offset);
+				sumlen = c->sector_size - je32_to_cpu(sm->offset);
+			}
+		} else {
+			/* If NAND flash, read a whole page of it. Else just the end */
+			if (c->wbuf_pagesize)
+				buf_len = c->wbuf_pagesize;
+			else
+				buf_len = sizeof(*sm);
+
+			/* Read as much as we want into the _end_ of the preallocated buffer */
+			err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, 
+						  jeb->offset + c->sector_size - buf_len,
+						  buf_len);				
+			if (err)
 				return err;
 				return err;
+
+			sm = (void *)buf + buf_size - sizeof(*sm);
+			if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) {
+				sumlen = c->sector_size - je32_to_cpu(sm->offset);
+				sumptr = buf + buf_size - sumlen;
+
+				/* Now, make sure the summary itself is available */
+				if (sumlen > buf_size) {
+					/* Need to kmalloc for this. */
+					sumptr = kmalloc(sumlen, GFP_KERNEL);
+					if (!sumptr)
+						return -ENOMEM;
+					memcpy(sumptr + sumlen - buf_len, buf + buf_size - buf_len, buf_len);
+				}
+				if (buf_len < sumlen) {
+					/* Need to read more so that the entire summary node is present */
+					err = jffs2_fill_scan_buf(c, sumptr, 
+								  jeb->offset + c->sector_size - sumlen,
+								  sumlen - buf_len);				
+					if (err)
+						return err;
+				}
 			}
 			}
+
 		}
 		}
 
 
-		kfree(sm);
+		if (sumptr) {
+			err = jffs2_sum_scan_sumnode(c, jeb, sumptr, sumlen, &pseudo_random);
 
 
-		ofs = jeb->offset;
-		prevofs = jeb->offset - 1;
+			if (buf_size && sumlen > buf_size)
+				kfree(sumptr);
+			/* If it returns with a real error, bail. 
+			   If it returns positive, that's a block classification
+			   (i.e. BLK_STATE_xxx) so return that too.
+			   If it returns zero, fall through to full scan. */
+			if (err)
+				return err;
+		}
 	}
 	}
 
 
 	buf_ofs = jeb->offset;
 	buf_ofs = jeb->offset;
 
 
 	if (!buf_size) {
 	if (!buf_size) {
+		/* This is the XIP case -- we're reading _directly_ from the flash chip */
 		buf_len = c->sector_size;
 		buf_len = c->sector_size;
-
-		if (jffs2_sum_active()) {
-			/* must reread because of summary test */
-			err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
-			if (err)
-				return err;
-		}
-
 	} else {
 	} else {
 		buf_len = EMPTY_SCAN_SIZE(c->sector_size);
 		buf_len = EMPTY_SCAN_SIZE(c->sector_size);
 		err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
 		err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
@@ -418,7 +562,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
 	if (ofs) {
 	if (ofs) {
 		D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
 		D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
 			  jeb->offset + ofs));
 			  jeb->offset + ofs));
-		DIRTY_SPACE(ofs);
+		if ((err = jffs2_prealloc_raw_node_refs(c, jeb, 1)))
+			return err;
+		if ((err = jffs2_scan_dirty_space(c, jeb, ofs)))
+			return err;
 	}
 	}
 
 
 	/* Now ofs is a complete physical flash offset as it always was... */
 	/* Now ofs is a complete physical flash offset as it always was... */
@@ -433,6 +580,11 @@ scan_more:
 
 
 		jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
 		jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
 
 
+		/* Make sure there are node refs available for use */
+		err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
+		if (err)
+			return err;
+
 		cond_resched();
 		cond_resched();
 
 
 		if (ofs & 3) {
 		if (ofs & 3) {
@@ -442,7 +594,8 @@ scan_more:
 		}
 		}
 		if (ofs == prevofs) {
 		if (ofs == prevofs) {
 			printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs);
 			printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs);
-			DIRTY_SPACE(4);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
 			ofs += 4;
 			ofs += 4;
 			continue;
 			continue;
 		}
 		}
@@ -451,7 +604,8 @@ scan_more:
 		if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
 		if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
 			D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
 			D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
 				  jeb->offset, c->sector_size, ofs, sizeof(*node)));
 				  jeb->offset, c->sector_size, ofs, sizeof(*node)));
-			DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
+			if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs)))
+				return err;
 			break;
 			break;
 		}
 		}
 
 
@@ -481,7 +635,8 @@ scan_more:
 				if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
 				if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
 					printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
 					printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
 					       empty_start, ofs);
 					       empty_start, ofs);
-					DIRTY_SPACE(ofs-empty_start);
+					if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start)))
+						return err;
 					goto scan_more;
 					goto scan_more;
 				}
 				}
 
 
@@ -494,7 +649,7 @@ scan_more:
 			/* If we're only checking the beginning of a block with a cleanmarker,
 			/* If we're only checking the beginning of a block with a cleanmarker,
 			   bail now */
 			   bail now */
 			if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
 			if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
-			    c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
+			    c->cleanmarker_size && !jeb->dirty_size && !ref_next(jeb->first_node)) {
 				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
 				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
 				return BLK_STATE_CLEANMARKER;
 				return BLK_STATE_CLEANMARKER;
 			}
 			}
@@ -518,20 +673,23 @@ scan_more:
 
 
 		if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
 		if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
 			printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
 			printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
-			DIRTY_SPACE(4);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
 			ofs += 4;
 			ofs += 4;
 			continue;
 			continue;
 		}
 		}
 		if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
 		if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
 			D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
 			D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
-			DIRTY_SPACE(4);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
 			ofs += 4;
 			ofs += 4;
 			continue;
 			continue;
 		}
 		}
 		if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
 		if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
 			printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
 			printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
 			printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
 			printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
-			DIRTY_SPACE(4);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
 			ofs += 4;
 			ofs += 4;
 			continue;
 			continue;
 		}
 		}
@@ -540,7 +698,8 @@ scan_more:
 			noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
 			noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
 				     JFFS2_MAGIC_BITMASK, ofs,
 				     JFFS2_MAGIC_BITMASK, ofs,
 				     je16_to_cpu(node->magic));
 				     je16_to_cpu(node->magic));
-			DIRTY_SPACE(4);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
 			ofs += 4;
 			ofs += 4;
 			continue;
 			continue;
 		}
 		}
@@ -557,7 +716,8 @@ scan_more:
 				     je32_to_cpu(node->totlen),
 				     je32_to_cpu(node->totlen),
 				     je32_to_cpu(node->hdr_crc),
 				     je32_to_cpu(node->hdr_crc),
 				     hdr_crc);
 				     hdr_crc);
-			DIRTY_SPACE(4);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
 			ofs += 4;
 			ofs += 4;
 			continue;
 			continue;
 		}
 		}
@@ -568,7 +728,8 @@ scan_more:
 			printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
 			printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
 			       ofs, je32_to_cpu(node->totlen));
 			       ofs, je32_to_cpu(node->totlen));
 			printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
 			printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
-			DIRTY_SPACE(4);
+			if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
+				return err;
 			ofs += 4;
 			ofs += 4;
 			continue;
 			continue;
 		}
 		}
@@ -576,7 +737,8 @@ scan_more:
 		if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
 		if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
 			/* Wheee. This is an obsoleted node */
 			/* Wheee. This is an obsoleted node */
 			D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
 			D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
-			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+			if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
+				return err;
 			ofs += PAD(je32_to_cpu(node->totlen));
 			ofs += PAD(je32_to_cpu(node->totlen));
 			continue;
 			continue;
 		}
 		}
@@ -614,30 +776,59 @@ scan_more:
 			ofs += PAD(je32_to_cpu(node->totlen));
 			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 			break;
 
 
+#ifdef CONFIG_JFFS2_FS_XATTR
+		case JFFS2_NODETYPE_XATTR:
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (xattr node)"
+					  " left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			err = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs, s);
+			if (err)
+				return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			break;
+		case JFFS2_NODETYPE_XREF:
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (xref node)"
+					  " left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			err = jffs2_scan_xref_node(c, jeb, (void *)node, ofs, s);
+			if (err)
+				return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
+			break;
+#endif	/* CONFIG_JFFS2_FS_XATTR */
+
 		case JFFS2_NODETYPE_CLEANMARKER:
 		case JFFS2_NODETYPE_CLEANMARKER:
 			D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
 			D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
 			if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
 			if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
 				       ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
 				       ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
-				DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
+					return err;
 				ofs += PAD(sizeof(struct jffs2_unknown_node));
 				ofs += PAD(sizeof(struct jffs2_unknown_node));
 			} else if (jeb->first_node) {
 			} else if (jeb->first_node) {
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
-				DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
+					return err;
 				ofs += PAD(sizeof(struct jffs2_unknown_node));
 				ofs += PAD(sizeof(struct jffs2_unknown_node));
 			} else {
 			} else {
-				struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
-				if (!marker_ref) {
-					printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n");
-					return -ENOMEM;
-				}
-				marker_ref->next_in_ino = NULL;
-				marker_ref->next_phys = NULL;
-				marker_ref->flash_offset = ofs | REF_NORMAL;
-				marker_ref->__totlen = c->cleanmarker_size;
-				jeb->first_node = jeb->last_node = marker_ref;
+				jffs2_link_node_ref(c, jeb, ofs | REF_NORMAL, c->cleanmarker_size, NULL);
 
 
-				USED_SPACE(PAD(c->cleanmarker_size));
 				ofs += PAD(c->cleanmarker_size);
 				ofs += PAD(c->cleanmarker_size);
 			}
 			}
 			break;
 			break;
@@ -645,7 +836,8 @@ scan_more:
 		case JFFS2_NODETYPE_PADDING:
 		case JFFS2_NODETYPE_PADDING:
 			if (jffs2_sum_active())
 			if (jffs2_sum_active())
 				jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen));
 				jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen));
-			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+			if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
+				return err;
 			ofs += PAD(je32_to_cpu(node->totlen));
 			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 			break;
 
 
@@ -656,7 +848,8 @@ scan_more:
 			        c->flags |= JFFS2_SB_FLAG_RO;
 			        c->flags |= JFFS2_SB_FLAG_RO;
 				if (!(jffs2_is_readonly(c)))
 				if (!(jffs2_is_readonly(c)))
 					return -EROFS;
 					return -EROFS;
-				DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
+					return err;
 				ofs += PAD(je32_to_cpu(node->totlen));
 				ofs += PAD(je32_to_cpu(node->totlen));
 				break;
 				break;
 
 
@@ -666,15 +859,21 @@ scan_more:
 
 
 			case JFFS2_FEATURE_RWCOMPAT_DELETE:
 			case JFFS2_FEATURE_RWCOMPAT_DELETE:
 				D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
 				D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
-				DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+				if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
+					return err;
 				ofs += PAD(je32_to_cpu(node->totlen));
 				ofs += PAD(je32_to_cpu(node->totlen));
 				break;
 				break;
 
 
-			case JFFS2_FEATURE_RWCOMPAT_COPY:
+			case JFFS2_FEATURE_RWCOMPAT_COPY: {
 				D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
 				D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
-				USED_SPACE(PAD(je32_to_cpu(node->totlen)));
+
+				jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(node->totlen)), NULL);
+
+				/* We can't summarise nodes we don't grok */
+				jffs2_sum_disable_collecting(s);
 				ofs += PAD(je32_to_cpu(node->totlen));
 				ofs += PAD(je32_to_cpu(node->totlen));
 				break;
 				break;
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -687,9 +886,9 @@ scan_more:
 		}
 		}
 	}
 	}
 
 
-	D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset,
-		  jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
-
+	D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n",
+		  jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size));
+	
 	/* mark_node_obsolete can add to wasted !! */
 	/* mark_node_obsolete can add to wasted !! */
 	if (jeb->wasted_size) {
 	if (jeb->wasted_size) {
 		jeb->dirty_size += jeb->wasted_size;
 		jeb->dirty_size += jeb->wasted_size;
@@ -730,9 +929,9 @@ struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uin
 static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 				 struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s)
 				 struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s)
 {
 {
-	struct jffs2_raw_node_ref *raw;
 	struct jffs2_inode_cache *ic;
 	struct jffs2_inode_cache *ic;
 	uint32_t ino = je32_to_cpu(ri->ino);
 	uint32_t ino = je32_to_cpu(ri->ino);
+	int err;
 
 
 	D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
 	D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
 
 
@@ -745,12 +944,6 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
 	   Which means that the _full_ amount of time to get to proper write mode with GC
 	   Which means that the _full_ amount of time to get to proper write mode with GC
 	   operational may actually be _longer_ than before. Sucks to be me. */
 	   operational may actually be _longer_ than before. Sucks to be me. */
 
 
-	raw = jffs2_alloc_raw_node_ref();
-	if (!raw) {
-		printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
-		return -ENOMEM;
-	}
-
 	ic = jffs2_get_ino_cache(c, ino);
 	ic = jffs2_get_ino_cache(c, ino);
 	if (!ic) {
 	if (!ic) {
 		/* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
 		/* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
@@ -762,30 +955,17 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
 			printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
 			printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
 			       ofs, je32_to_cpu(ri->node_crc), crc);
 			       ofs, je32_to_cpu(ri->node_crc), crc);
 			/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
 			/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
-			DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
-			jffs2_free_raw_node_ref(raw);
+			if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(ri->totlen)))))
+				return err;
 			return 0;
 			return 0;
 		}
 		}
 		ic = jffs2_scan_make_ino_cache(c, ino);
 		ic = jffs2_scan_make_ino_cache(c, ino);
-		if (!ic) {
-			jffs2_free_raw_node_ref(raw);
+		if (!ic)
 			return -ENOMEM;
 			return -ENOMEM;
-		}
 	}
 	}
 
 
 	/* Wheee. It worked */
 	/* Wheee. It worked */
-
-	raw->flash_offset = ofs | REF_UNCHECKED;
-	raw->__totlen = PAD(je32_to_cpu(ri->totlen));
-	raw->next_phys = NULL;
-	raw->next_in_ino = ic->nodes;
-
-	ic->nodes = raw;
-	if (!jeb->first_node)
-		jeb->first_node = raw;
-	if (jeb->last_node)
-		jeb->last_node->next_phys = raw;
-	jeb->last_node = raw;
+	jffs2_link_node_ref(c, jeb, ofs | REF_UNCHECKED, PAD(je32_to_cpu(ri->totlen)), ic);
 
 
 	D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
 	D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
 		  je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
 		  je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
@@ -794,8 +974,6 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
 
 
 	pseudo_random += je32_to_cpu(ri->version);
 	pseudo_random += je32_to_cpu(ri->version);
 
 
-	UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
-
 	if (jffs2_sum_active()) {
 	if (jffs2_sum_active()) {
 		jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset);
 		jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset);
 	}
 	}
@@ -806,10 +984,10 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
 static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 				  struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s)
 				  struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s)
 {
 {
-	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dirent *fd;
 	struct jffs2_full_dirent *fd;
 	struct jffs2_inode_cache *ic;
 	struct jffs2_inode_cache *ic;
 	uint32_t crc;
 	uint32_t crc;
+	int err;
 
 
 	D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
 	D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
 
 
@@ -821,7 +999,8 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
 		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
 		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
 		       ofs, je32_to_cpu(rd->node_crc), crc);
 		       ofs, je32_to_cpu(rd->node_crc), crc);
 		/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
 		/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
-		DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+		if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen)))))
+			return err;
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -842,40 +1021,23 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
 		jffs2_free_full_dirent(fd);
 		jffs2_free_full_dirent(fd);
 		/* FIXME: Why do we believe totlen? */
 		/* FIXME: Why do we believe totlen? */
 		/* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
 		/* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
-		DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+		if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen)))))
+			return err;
 		return 0;
 		return 0;
 	}
 	}
-	raw = jffs2_alloc_raw_node_ref();
-	if (!raw) {
-		jffs2_free_full_dirent(fd);
-		printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
-		return -ENOMEM;
-	}
 	ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
 	ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
 	if (!ic) {
 	if (!ic) {
 		jffs2_free_full_dirent(fd);
 		jffs2_free_full_dirent(fd);
-		jffs2_free_raw_node_ref(raw);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	raw->__totlen = PAD(je32_to_cpu(rd->totlen));
-	raw->flash_offset = ofs | REF_PRISTINE;
-	raw->next_phys = NULL;
-	raw->next_in_ino = ic->nodes;
-	ic->nodes = raw;
-	if (!jeb->first_node)
-		jeb->first_node = raw;
-	if (jeb->last_node)
-		jeb->last_node->next_phys = raw;
-	jeb->last_node = raw;
+	fd->raw = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rd->totlen)), ic);
 
 
-	fd->raw = raw;
 	fd->next = NULL;
 	fd->next = NULL;
 	fd->version = je32_to_cpu(rd->version);
 	fd->version = je32_to_cpu(rd->version);
 	fd->ino = je32_to_cpu(rd->ino);
 	fd->ino = je32_to_cpu(rd->ino);
 	fd->nhash = full_name_hash(fd->name, rd->nsize);
 	fd->nhash = full_name_hash(fd->name, rd->nsize);
 	fd->type = rd->type;
 	fd->type = rd->type;
-	USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
 	jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 	jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 
 
 	if (jffs2_sum_active()) {
 	if (jffs2_sum_active()) {

+ 82 - 0
fs/jffs2/security.c

@@ -0,0 +1,82 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2006  NEC Corporation
+ *
+ * Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/crc32.h>
+#include <linux/jffs2.h>
+#include <linux/xattr.h>
+#include <linux/mtd/mtd.h>
+#include <linux/security.h>
+#include "nodelist.h"
+
+/* ---- Initial Security Label Attachment -------------- */
+int jffs2_init_security(struct inode *inode, struct inode *dir)
+{
+	int rc;
+	size_t len;
+	void *value;
+	char *name;
+
+	rc = security_inode_init_security(inode, dir, &name, &value, &len);
+	if (rc) {
+		if (rc == -EOPNOTSUPP)
+			return 0;
+		return rc;
+	}
+	rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
+
+        kfree(name);
+        kfree(value);
+        return rc;
+}
+
+/* ---- XATTR Handler for "security.*" ----------------- */
+static int jffs2_security_getxattr(struct inode *inode, const char *name,
+				   void *buffer, size_t size)
+{
+	if (!strcmp(name, ""))
+		return -EINVAL;
+
+	return do_jffs2_getxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size);
+}
+
+static int jffs2_security_setxattr(struct inode *inode, const char *name, const void *buffer,
+				   size_t size, int flags)
+{
+	if (!strcmp(name, ""))
+		return -EINVAL;
+
+	return do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size, flags);
+}
+
+static size_t jffs2_security_listxattr(struct inode *inode, char *list, size_t list_size,
+				       const char *name, size_t name_len)
+{
+	size_t retlen = XATTR_SECURITY_PREFIX_LEN + name_len + 1;
+
+	if (list && retlen <= list_size) {
+		strcpy(list, XATTR_SECURITY_PREFIX);
+		strcpy(list + XATTR_SECURITY_PREFIX_LEN, name);
+	}
+
+	return retlen;
+}
+
+struct xattr_handler jffs2_security_xattr_handler = {
+	.prefix = XATTR_SECURITY_PREFIX,
+	.list = jffs2_security_listxattr,
+	.set = jffs2_security_setxattr,
+	.get = jffs2_security_getxattr
+};

+ 303 - 181
fs/jffs2/summary.c

@@ -5,6 +5,7 @@
  *                     Zoltan Sogor <weth@inf.u-szeged.hu>,
  *                     Zoltan Sogor <weth@inf.u-szeged.hu>,
  *                     Patrik Kluba <pajko@halom.u-szeged.hu>,
  *                     Patrik Kluba <pajko@halom.u-szeged.hu>,
  *                     University of Szeged, Hungary
  *                     University of Szeged, Hungary
+ *               2005  KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  *
@@ -81,6 +82,19 @@ static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item)
 			dbg_summary("dirent (%u) added to summary\n",
 			dbg_summary("dirent (%u) added to summary\n",
 						je32_to_cpu(item->d.ino));
 						je32_to_cpu(item->d.ino));
 			break;
 			break;
+#ifdef CONFIG_JFFS2_FS_XATTR
+		case JFFS2_NODETYPE_XATTR:
+			s->sum_size += JFFS2_SUMMARY_XATTR_SIZE;
+			s->sum_num++;
+			dbg_summary("xattr (xid=%u, version=%u) added to summary\n",
+				    je32_to_cpu(item->x.xid), je32_to_cpu(item->x.version));
+			break;
+		case JFFS2_NODETYPE_XREF:
+			s->sum_size += JFFS2_SUMMARY_XREF_SIZE;
+			s->sum_num++;
+			dbg_summary("xref added to summary\n");
+			break;
+#endif
 		default:
 		default:
 			JFFS2_WARNING("UNKNOWN node type %u\n",
 			JFFS2_WARNING("UNKNOWN node type %u\n",
 					    je16_to_cpu(item->u.nodetype));
 					    je16_to_cpu(item->u.nodetype));
@@ -141,6 +155,40 @@ int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *r
 	return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
 	return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
 }
 }
 
 
+#ifdef CONFIG_JFFS2_FS_XATTR
+int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs)
+{
+	struct jffs2_sum_xattr_mem *temp;
+
+	temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
+	if (!temp)
+		return -ENOMEM;
+
+	temp->nodetype = rx->nodetype;
+	temp->xid = rx->xid;
+	temp->version = rx->version;
+	temp->offset = cpu_to_je32(ofs);
+	temp->totlen = rx->totlen;
+	temp->next = NULL;
+
+	return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+
+int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs)
+{
+	struct jffs2_sum_xref_mem *temp;
+
+	temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
+	if (!temp)
+		return -ENOMEM;
+
+	temp->nodetype = rr->nodetype;
+	temp->offset = cpu_to_je32(ofs);
+	temp->next = NULL;
+
+	return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+#endif
 /* Cleanup every collected summary information */
 /* Cleanup every collected summary information */
 
 
 static void jffs2_sum_clean_collected(struct jffs2_summary *s)
 static void jffs2_sum_clean_collected(struct jffs2_summary *s)
@@ -259,7 +307,40 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
 
 
 			return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
 			return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
 		}
 		}
+#ifdef CONFIG_JFFS2_FS_XATTR
+		case JFFS2_NODETYPE_XATTR: {
+			struct jffs2_sum_xattr_mem *temp;
+			if (je32_to_cpu(node->x.version) == 0xffffffff)
+				return 0;
+			temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
+			if (!temp)
+				goto no_mem;
+
+			temp->nodetype = node->x.nodetype;
+			temp->xid = node->x.xid;
+			temp->version = node->x.version;
+			temp->totlen = node->x.totlen;
+			temp->offset = cpu_to_je32(ofs);
+			temp->next = NULL;
+
+			return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+		}
+		case JFFS2_NODETYPE_XREF: {
+			struct jffs2_sum_xref_mem *temp;
+
+			if (je32_to_cpu(node->r.ino) == 0xffffffff
+			    && je32_to_cpu(node->r.xid) == 0xffffffff)
+				return 0;
+			temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
+			if (!temp)
+				goto no_mem;
+			temp->nodetype = node->r.nodetype;
+			temp->offset = cpu_to_je32(ofs);
+			temp->next = NULL;
 
 
+			return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+		}
+#endif
 		case JFFS2_NODETYPE_PADDING:
 		case JFFS2_NODETYPE_PADDING:
 			dbg_summary("node PADDING\n");
 			dbg_summary("node PADDING\n");
 			c->summary->sum_padded += je32_to_cpu(node->u.totlen);
 			c->summary->sum_padded += je32_to_cpu(node->u.totlen);
@@ -288,23 +369,41 @@ no_mem:
 	return -ENOMEM;
 	return -ENOMEM;
 }
 }
 
 
+static struct jffs2_raw_node_ref *sum_link_node_ref(struct jffs2_sb_info *c,
+						    struct jffs2_eraseblock *jeb,
+						    uint32_t ofs, uint32_t len,
+						    struct jffs2_inode_cache *ic)
+{
+	/* If there was a gap, mark it dirty */
+	if ((ofs & ~3) > c->sector_size - jeb->free_size) {
+		/* Ew. Summary doesn't actually tell us explicitly about dirty space */
+		jffs2_scan_dirty_space(c, jeb, (ofs & ~3) - (c->sector_size - jeb->free_size));
+	}
+
+	return jffs2_link_node_ref(c, jeb, jeb->offset + ofs, len, ic);
+}
 
 
 /* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */
 /* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */
 
 
 static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 				struct jffs2_raw_summary *summary, uint32_t *pseudo_random)
 				struct jffs2_raw_summary *summary, uint32_t *pseudo_random)
 {
 {
-	struct jffs2_raw_node_ref *raw;
 	struct jffs2_inode_cache *ic;
 	struct jffs2_inode_cache *ic;
 	struct jffs2_full_dirent *fd;
 	struct jffs2_full_dirent *fd;
 	void *sp;
 	void *sp;
 	int i, ino;
 	int i, ino;
+	int err;
 
 
 	sp = summary->sum;
 	sp = summary->sum;
 
 
 	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);
 
 
+		/* Make sure there's a spare ref for dirty space */
+		err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
+		if (err)
+			return err;
+
 		switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
 		switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
 			case JFFS2_NODETYPE_INODE: {
 			case JFFS2_NODETYPE_INODE: {
 				struct jffs2_sum_inode_flash *spi;
 				struct jffs2_sum_inode_flash *spi;
@@ -312,38 +411,20 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
 
 
 				ino = je32_to_cpu(spi->inode);
 				ino = je32_to_cpu(spi->inode);
 
 
-				dbg_summary("Inode at 0x%08x\n",
-							jeb->offset + je32_to_cpu(spi->offset));
-
-				raw = jffs2_alloc_raw_node_ref();
-				if (!raw) {
-					JFFS2_NOTICE("allocation of node reference failed\n");
-					kfree(summary);
-					return -ENOMEM;
-				}
+				dbg_summary("Inode at 0x%08x-0x%08x\n",
+					    jeb->offset + je32_to_cpu(spi->offset),
+					    jeb->offset + je32_to_cpu(spi->offset) + je32_to_cpu(spi->totlen));
 
 
 				ic = jffs2_scan_make_ino_cache(c, ino);
 				ic = jffs2_scan_make_ino_cache(c, ino);
 				if (!ic) {
 				if (!ic) {
 					JFFS2_NOTICE("scan_make_ino_cache failed\n");
 					JFFS2_NOTICE("scan_make_ino_cache failed\n");
-					jffs2_free_raw_node_ref(raw);
-					kfree(summary);
 					return -ENOMEM;
 					return -ENOMEM;
 				}
 				}
 
 
-				raw->flash_offset = (jeb->offset + je32_to_cpu(spi->offset)) | REF_UNCHECKED;
-				raw->__totlen = PAD(je32_to_cpu(spi->totlen));
-				raw->next_phys = NULL;
-				raw->next_in_ino = ic->nodes;
-
-				ic->nodes = raw;
-				if (!jeb->first_node)
-					jeb->first_node = raw;
-				if (jeb->last_node)
-					jeb->last_node->next_phys = raw;
-				jeb->last_node = raw;
-				*pseudo_random += je32_to_cpu(spi->version);
+				sum_link_node_ref(c, jeb, je32_to_cpu(spi->offset) | REF_UNCHECKED,
+						  PAD(je32_to_cpu(spi->totlen)), ic);
 
 
-				UNCHECKED_SPACE(PAD(je32_to_cpu(spi->totlen)));
+				*pseudo_random += je32_to_cpu(spi->version);
 
 
 				sp += JFFS2_SUMMARY_INODE_SIZE;
 				sp += JFFS2_SUMMARY_INODE_SIZE;
 
 
@@ -354,52 +435,33 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
 				struct jffs2_sum_dirent_flash *spd;
 				struct jffs2_sum_dirent_flash *spd;
 				spd = sp;
 				spd = sp;
 
 
-				dbg_summary("Dirent at 0x%08x\n",
-							jeb->offset + je32_to_cpu(spd->offset));
+				dbg_summary("Dirent at 0x%08x-0x%08x\n",
+					    jeb->offset + je32_to_cpu(spd->offset),
+					    jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen));
+
 
 
 				fd = jffs2_alloc_full_dirent(spd->nsize+1);
 				fd = jffs2_alloc_full_dirent(spd->nsize+1);
-				if (!fd) {
-					kfree(summary);
+				if (!fd)
 					return -ENOMEM;
 					return -ENOMEM;
-				}
 
 
 				memcpy(&fd->name, spd->name, spd->nsize);
 				memcpy(&fd->name, spd->name, spd->nsize);
 				fd->name[spd->nsize] = 0;
 				fd->name[spd->nsize] = 0;
 
 
-				raw = jffs2_alloc_raw_node_ref();
-				if (!raw) {
-					jffs2_free_full_dirent(fd);
-					JFFS2_NOTICE("allocation of node reference failed\n");
-					kfree(summary);
-					return -ENOMEM;
-				}
-
 				ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
 				ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
 				if (!ic) {
 				if (!ic) {
 					jffs2_free_full_dirent(fd);
 					jffs2_free_full_dirent(fd);
-					jffs2_free_raw_node_ref(raw);
-					kfree(summary);
 					return -ENOMEM;
 					return -ENOMEM;
 				}
 				}
 
 
-				raw->__totlen = PAD(je32_to_cpu(spd->totlen));
-				raw->flash_offset = (jeb->offset + je32_to_cpu(spd->offset)) | REF_PRISTINE;
-				raw->next_phys = NULL;
-				raw->next_in_ino = ic->nodes;
-				ic->nodes = raw;
-				if (!jeb->first_node)
-					jeb->first_node = raw;
-				if (jeb->last_node)
-					jeb->last_node->next_phys = raw;
-				jeb->last_node = raw;
-
-				fd->raw = raw;
+				fd->raw = sum_link_node_ref(c, jeb,  je32_to_cpu(spd->offset) | REF_UNCHECKED,
+							    PAD(je32_to_cpu(spd->totlen)), ic);
+
 				fd->next = NULL;
 				fd->next = NULL;
 				fd->version = je32_to_cpu(spd->version);
 				fd->version = je32_to_cpu(spd->version);
 				fd->ino = je32_to_cpu(spd->ino);
 				fd->ino = je32_to_cpu(spd->ino);
 				fd->nhash = full_name_hash(fd->name, spd->nsize);
 				fd->nhash = full_name_hash(fd->name, spd->nsize);
 				fd->type = spd->type;
 				fd->type = spd->type;
-				USED_SPACE(PAD(je32_to_cpu(spd->totlen)));
+
 				jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 				jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 
 
 				*pseudo_random += je32_to_cpu(spd->version);
 				*pseudo_random += je32_to_cpu(spd->version);
@@ -408,48 +470,105 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
 
 
 				break;
 				break;
 			}
 			}
+#ifdef CONFIG_JFFS2_FS_XATTR
+			case JFFS2_NODETYPE_XATTR: {
+				struct jffs2_xattr_datum *xd;
+				struct jffs2_sum_xattr_flash *spx;
+
+				spx = (struct jffs2_sum_xattr_flash *)sp;
+				dbg_summary("xattr at %#08x-%#08x (xid=%u, version=%u)\n", 
+					    jeb->offset + je32_to_cpu(spx->offset),
+					    jeb->offset + je32_to_cpu(spx->offset) + je32_to_cpu(spx->totlen),
+					    je32_to_cpu(spx->xid), je32_to_cpu(spx->version));
+
+				xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid),
+								je32_to_cpu(spx->version));
+				if (IS_ERR(xd)) {
+					if (PTR_ERR(xd) == -EEXIST) {
+						/* a newer version of xd exists */
+						if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(spx->totlen))))
+							return err;
+						sp += JFFS2_SUMMARY_XATTR_SIZE;
+						break;
+					}
+					JFFS2_NOTICE("allocation of xattr_datum failed\n");
+					return PTR_ERR(xd);
+				}
+
+				xd->node = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
+							     PAD(je32_to_cpu(spx->totlen)), NULL);
+				/* FIXME */ xd->node->next_in_ino = (void *)xd;
+
+				*pseudo_random += je32_to_cpu(spx->xid);
+				sp += JFFS2_SUMMARY_XATTR_SIZE;
+
+				break;
+			}
+			case JFFS2_NODETYPE_XREF: {
+				struct jffs2_xattr_ref *ref;
+				struct jffs2_sum_xref_flash *spr;
+
+				spr = (struct jffs2_sum_xref_flash *)sp;
+				dbg_summary("xref at %#08x-%#08x\n",
+					    jeb->offset + je32_to_cpu(spr->offset),
+					    jeb->offset + je32_to_cpu(spr->offset) + 
+					    (uint32_t)PAD(sizeof(struct jffs2_raw_xref)));
+
+				ref = jffs2_alloc_xattr_ref();
+				if (!ref) {
+					JFFS2_NOTICE("allocation of xattr_datum failed\n");
+					return -ENOMEM;
+				}
+				ref->ino = 0xfffffffe;
+				ref->xid = 0xfffffffd;
+				ref->next = c->xref_temp;
+				c->xref_temp = ref;
 
 
+				ref->node = sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED,
+							      PAD(sizeof(struct jffs2_raw_xref)), NULL);
+				/* FIXME */ ref->node->next_in_ino = (void *)ref;
+
+				*pseudo_random += ref->node->flash_offset;
+				sp += JFFS2_SUMMARY_XREF_SIZE;
+
+				break;
+			}
+#endif
 			default : {
 			default : {
-				JFFS2_WARNING("Unsupported node type found in summary! Exiting...");
-				kfree(summary);
-				return -EIO;
+				uint16_t nodetype = je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype);
+				JFFS2_WARNING("Unsupported node type %x found in summary! Exiting...\n", nodetype);
+				if ((nodetype & JFFS2_COMPAT_MASK) == JFFS2_FEATURE_INCOMPAT)
+					return -EIO;
+
+				/* For compatible node types, just fall back to the full scan */
+				c->wasted_size -= jeb->wasted_size;
+				c->free_size += c->sector_size - jeb->free_size;
+				c->used_size -= jeb->used_size;
+				c->dirty_size -= jeb->dirty_size;
+				jeb->wasted_size = jeb->used_size = jeb->dirty_size = 0;
+				jeb->free_size = c->sector_size;
+
+				jffs2_free_jeb_node_refs(c, jeb);
+				return -ENOTRECOVERABLE;
 			}
 			}
 		}
 		}
 	}
 	}
-
-	kfree(summary);
 	return 0;
 	return 0;
 }
 }
 
 
 /* Process the summary node - called from jffs2_scan_eraseblock() */
 /* Process the summary node - called from jffs2_scan_eraseblock() */
-
 int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-				uint32_t ofs, uint32_t *pseudo_random)
+			   struct jffs2_raw_summary *summary, uint32_t sumsize,
+			   uint32_t *pseudo_random)
 {
 {
 	struct jffs2_unknown_node crcnode;
 	struct jffs2_unknown_node crcnode;
-	struct jffs2_raw_node_ref *cache_ref;
-	struct jffs2_raw_summary *summary;
-	int ret, sumsize;
+	int ret, ofs;
 	uint32_t crc;
 	uint32_t crc;
 
 
-	sumsize = c->sector_size - ofs;
-	ofs += jeb->offset;
+	ofs = c->sector_size - sumsize;
 
 
 	dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n",
 	dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n",
-				jeb->offset, ofs, sumsize);
-
-	summary = kmalloc(sumsize, GFP_KERNEL);
-
-	if (!summary) {
-		return -ENOMEM;
-	}
-
-	ret = jffs2_fill_scan_buf(c, (unsigned char *)summary, ofs, sumsize);
-
-	if (ret) {
-		kfree(summary);
-		return ret;
-	}
+		    jeb->offset, jeb->offset + ofs, sumsize);
 
 
 	/* OK, now check for node validity and CRC */
 	/* OK, now check for node validity and CRC */
 	crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
 	crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -486,66 +605,49 @@ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
 
 
 		dbg_summary("Summary : CLEANMARKER node \n");
 		dbg_summary("Summary : CLEANMARKER node \n");
 
 
+		ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
+		if (ret)
+			return ret;
+
 		if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) {
 		if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) {
 			dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n",
 			dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n",
 				je32_to_cpu(summary->cln_mkr), c->cleanmarker_size);
 				je32_to_cpu(summary->cln_mkr), c->cleanmarker_size);
-			UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr)));
+			if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr)))))
+				return ret;
 		} else if (jeb->first_node) {
 		} else if (jeb->first_node) {
 			dbg_summary("CLEANMARKER node not first node in block "
 			dbg_summary("CLEANMARKER node not first node in block "
 					"(0x%08x)\n", jeb->offset);
 					"(0x%08x)\n", jeb->offset);
-			UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr)));
+			if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr)))))
+				return ret;
 		} else {
 		} else {
-			struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
-
-			if (!marker_ref) {
-				JFFS2_NOTICE("Failed to allocate node ref for clean marker\n");
-				kfree(summary);
-				return -ENOMEM;
-			}
-
-			marker_ref->next_in_ino = NULL;
-			marker_ref->next_phys = NULL;
-			marker_ref->flash_offset = jeb->offset | REF_NORMAL;
-			marker_ref->__totlen = je32_to_cpu(summary->cln_mkr);
-			jeb->first_node = jeb->last_node = marker_ref;
-
-			USED_SPACE( PAD(je32_to_cpu(summary->cln_mkr)) );
+			jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL,
+					    je32_to_cpu(summary->cln_mkr), NULL);
 		}
 		}
 	}
 	}
 
 
-	if (je32_to_cpu(summary->padded)) {
-		DIRTY_SPACE(je32_to_cpu(summary->padded));
-	}
-
 	ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random);
 	ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random);
+	/* -ENOTRECOVERABLE isn't a fatal error -- it means we should do a full
+	   scan of this eraseblock. So return zero */
+	if (ret == -ENOTRECOVERABLE)
+		return 0;
 	if (ret)
 	if (ret)
-		return ret;
+		return ret;		/* real error */
 
 
 	/* for PARANOIA_CHECK */
 	/* for PARANOIA_CHECK */
-	cache_ref = jffs2_alloc_raw_node_ref();
-
-	if (!cache_ref) {
-		JFFS2_NOTICE("Failed to allocate node ref for cache\n");
-		return -ENOMEM;
-	}
-
-	cache_ref->next_in_ino = NULL;
-	cache_ref->next_phys = NULL;
-	cache_ref->flash_offset = ofs | REF_NORMAL;
-	cache_ref->__totlen = sumsize;
-
-	if (!jeb->first_node)
-		jeb->first_node = cache_ref;
-	if (jeb->last_node)
-		jeb->last_node->next_phys = cache_ref;
-	jeb->last_node = cache_ref;
+	ret = jffs2_prealloc_raw_node_refs(c, jeb, 2);
+	if (ret)
+		return ret;
 
 
-	USED_SPACE(sumsize);
+	sum_link_node_ref(c, jeb, ofs | REF_NORMAL, sumsize, NULL);
 
 
-	jeb->wasted_size += jeb->free_size;
-	c->wasted_size += jeb->free_size;
-	c->free_size -= jeb->free_size;
-	jeb->free_size = 0;
+	if (unlikely(jeb->free_size)) {
+		JFFS2_WARNING("Free size 0x%x bytes in eraseblock @0x%08x with summary?\n",
+			      jeb->free_size, jeb->offset);
+		jeb->wasted_size += jeb->free_size;
+		c->wasted_size += jeb->free_size;
+		c->free_size -= jeb->free_size;
+		jeb->free_size = 0;
+	}
 
 
 	return jffs2_scan_classify_jeb(c, jeb);
 	return jffs2_scan_classify_jeb(c, jeb);
 
 
@@ -564,6 +666,7 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
 	union jffs2_sum_mem *temp;
 	union jffs2_sum_mem *temp;
 	struct jffs2_sum_marker *sm;
 	struct jffs2_sum_marker *sm;
 	struct kvec vecs[2];
 	struct kvec vecs[2];
+	uint32_t sum_ofs;
 	void *wpage;
 	void *wpage;
 	int ret;
 	int ret;
 	size_t retlen;
 	size_t retlen;
@@ -581,16 +684,17 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
 	wpage = c->summary->sum_buf;
 	wpage = c->summary->sum_buf;
 
 
 	while (c->summary->sum_num) {
 	while (c->summary->sum_num) {
+		temp = c->summary->sum_list_head;
 
 
-		switch (je16_to_cpu(c->summary->sum_list_head->u.nodetype)) {
+		switch (je16_to_cpu(temp->u.nodetype)) {
 			case JFFS2_NODETYPE_INODE: {
 			case JFFS2_NODETYPE_INODE: {
 				struct jffs2_sum_inode_flash *sino_ptr = wpage;
 				struct jffs2_sum_inode_flash *sino_ptr = wpage;
 
 
-				sino_ptr->nodetype = c->summary->sum_list_head->i.nodetype;
-				sino_ptr->inode = c->summary->sum_list_head->i.inode;
-				sino_ptr->version = c->summary->sum_list_head->i.version;
-				sino_ptr->offset = c->summary->sum_list_head->i.offset;
-				sino_ptr->totlen = c->summary->sum_list_head->i.totlen;
+				sino_ptr->nodetype = temp->i.nodetype;
+				sino_ptr->inode = temp->i.inode;
+				sino_ptr->version = temp->i.version;
+				sino_ptr->offset = temp->i.offset;
+				sino_ptr->totlen = temp->i.totlen;
 
 
 				wpage += JFFS2_SUMMARY_INODE_SIZE;
 				wpage += JFFS2_SUMMARY_INODE_SIZE;
 
 
@@ -600,30 +704,60 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
 			case JFFS2_NODETYPE_DIRENT: {
 			case JFFS2_NODETYPE_DIRENT: {
 				struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage;
 				struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage;
 
 
-				sdrnt_ptr->nodetype = c->summary->sum_list_head->d.nodetype;
-				sdrnt_ptr->totlen = c->summary->sum_list_head->d.totlen;
-				sdrnt_ptr->offset = c->summary->sum_list_head->d.offset;
-				sdrnt_ptr->pino = c->summary->sum_list_head->d.pino;
-				sdrnt_ptr->version = c->summary->sum_list_head->d.version;
-				sdrnt_ptr->ino = c->summary->sum_list_head->d.ino;
-				sdrnt_ptr->nsize = c->summary->sum_list_head->d.nsize;
-				sdrnt_ptr->type = c->summary->sum_list_head->d.type;
+				sdrnt_ptr->nodetype = temp->d.nodetype;
+				sdrnt_ptr->totlen = temp->d.totlen;
+				sdrnt_ptr->offset = temp->d.offset;
+				sdrnt_ptr->pino = temp->d.pino;
+				sdrnt_ptr->version = temp->d.version;
+				sdrnt_ptr->ino = temp->d.ino;
+				sdrnt_ptr->nsize = temp->d.nsize;
+				sdrnt_ptr->type = temp->d.type;
 
 
-				memcpy(sdrnt_ptr->name, c->summary->sum_list_head->d.name,
-							c->summary->sum_list_head->d.nsize);
+				memcpy(sdrnt_ptr->name, temp->d.name,
+							temp->d.nsize);
 
 
-				wpage += JFFS2_SUMMARY_DIRENT_SIZE(c->summary->sum_list_head->d.nsize);
+				wpage += JFFS2_SUMMARY_DIRENT_SIZE(temp->d.nsize);
 
 
 				break;
 				break;
 			}
 			}
+#ifdef CONFIG_JFFS2_FS_XATTR
+			case JFFS2_NODETYPE_XATTR: {
+				struct jffs2_sum_xattr_flash *sxattr_ptr = wpage;
+
+				temp = c->summary->sum_list_head;
+				sxattr_ptr->nodetype = temp->x.nodetype;
+				sxattr_ptr->xid = temp->x.xid;
+				sxattr_ptr->version = temp->x.version;
+				sxattr_ptr->offset = temp->x.offset;
+				sxattr_ptr->totlen = temp->x.totlen;
+
+				wpage += JFFS2_SUMMARY_XATTR_SIZE;
+				break;
+			}
+			case JFFS2_NODETYPE_XREF: {
+				struct jffs2_sum_xref_flash *sxref_ptr = wpage;
 
 
+				temp = c->summary->sum_list_head;
+				sxref_ptr->nodetype = temp->r.nodetype;
+				sxref_ptr->offset = temp->r.offset;
+
+				wpage += JFFS2_SUMMARY_XREF_SIZE;
+				break;
+			}
+#endif
 			default : {
 			default : {
-				BUG();	/* unknown node in summary information */
+				if ((je16_to_cpu(temp->u.nodetype) & JFFS2_COMPAT_MASK)
+				    == JFFS2_FEATURE_RWCOMPAT_COPY) {
+					dbg_summary("Writing unknown RWCOMPAT_COPY node type %x\n",
+						    je16_to_cpu(temp->u.nodetype));
+					jffs2_sum_disable_collecting(c->summary);
+				} else {
+					BUG();	/* unknown node in summary information */
+				}
 			}
 			}
 		}
 		}
 
 
-		temp = c->summary->sum_list_head;
-		c->summary->sum_list_head = c->summary->sum_list_head->u.next;
+		c->summary->sum_list_head = temp->u.next;
 		kfree(temp);
 		kfree(temp);
 
 
 		c->summary->sum_num--;
 		c->summary->sum_num--;
@@ -645,25 +779,34 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
 	vecs[1].iov_base = c->summary->sum_buf;
 	vecs[1].iov_base = c->summary->sum_buf;
 	vecs[1].iov_len = datasize;
 	vecs[1].iov_len = datasize;
 
 
-	dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n",
-			jeb->offset + c->sector_size - jeb->free_size);
+	sum_ofs = jeb->offset + c->sector_size - jeb->free_size;
 
 
-	spin_unlock(&c->erase_completion_lock);
-	ret = jffs2_flash_writev(c, vecs, 2, jeb->offset + c->sector_size -
-				jeb->free_size, &retlen, 0);
-	spin_lock(&c->erase_completion_lock);
+	dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n",
+		    sum_ofs);
 
 
+	ret = jffs2_flash_writev(c, vecs, 2, sum_ofs, &retlen, 0);
 
 
 	if (ret || (retlen != infosize)) {
 	if (ret || (retlen != infosize)) {
-		JFFS2_WARNING("Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
-			infosize, jeb->offset + c->sector_size - jeb->free_size, ret, retlen);
+
+		JFFS2_WARNING("Write of %u bytes at 0x%08x failed. returned %d, retlen %zd\n",
+			      infosize, sum_ofs, ret, retlen);
+
+		if (retlen) {
+			/* Waste remaining space */
+			spin_lock(&c->erase_completion_lock);
+			jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL);
+			spin_unlock(&c->erase_completion_lock);
+		}
 
 
 		c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
 		c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
-		WASTED_SPACE(infosize);
 
 
-		return 1;
+		return 0;
 	}
 	}
 
 
+	spin_lock(&c->erase_completion_lock);
+	jffs2_link_node_ref(c, jeb, sum_ofs | REF_NORMAL, infosize, NULL);
+	spin_unlock(&c->erase_completion_lock);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -671,13 +814,16 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
 
 
 int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
 int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
 {
 {
-	struct jffs2_raw_node_ref *summary_ref;
-	int datasize, infosize, padsize, ret;
+	int datasize, infosize, padsize;
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_eraseblock *jeb;
+	int ret;
 
 
 	dbg_summary("called\n");
 	dbg_summary("called\n");
 
 
+	spin_unlock(&c->erase_completion_lock);
+
 	jeb = c->nextblock;
 	jeb = c->nextblock;
+	jffs2_prealloc_raw_node_refs(c, jeb, 1);
 
 
 	if (!c->summary->sum_num || !c->summary->sum_list_head) {
 	if (!c->summary->sum_num || !c->summary->sum_list_head) {
 		JFFS2_WARNING("Empty summary info!!!\n");
 		JFFS2_WARNING("Empty summary info!!!\n");
@@ -696,35 +842,11 @@ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
 		jffs2_sum_disable_collecting(c->summary);
 		jffs2_sum_disable_collecting(c->summary);
 
 
 		JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize);
 		JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize);
+		spin_lock(&c->erase_completion_lock);
 		return 0;
 		return 0;
 	}
 	}
 
 
 	ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize);
 	ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize);
-	if (ret)
-		return 0; /* can't write out summary, block is marked as NOSUM_SIZE */
-
-	/* for ACCT_PARANOIA_CHECK */
-	spin_unlock(&c->erase_completion_lock);
-	summary_ref = jffs2_alloc_raw_node_ref();
 	spin_lock(&c->erase_completion_lock);
 	spin_lock(&c->erase_completion_lock);
-
-	if (!summary_ref) {
-		JFFS2_NOTICE("Failed to allocate node ref for summary\n");
-		return -ENOMEM;
-	}
-
-	summary_ref->next_in_ino = NULL;
-	summary_ref->next_phys = NULL;
-	summary_ref->flash_offset = (jeb->offset + c->sector_size - jeb->free_size) | REF_NORMAL;
-	summary_ref->__totlen = infosize;
-
-	if (!jeb->first_node)
-		jeb->first_node = summary_ref;
-	if (jeb->last_node)
-		jeb->last_node->next_phys = summary_ref;
-	jeb->last_node = summary_ref;
-
-	USED_SPACE(infosize);
-
-	return 0;
+	return ret;
 }
 }

+ 45 - 19
fs/jffs2/summary.h

@@ -18,23 +18,6 @@
 #include <linux/uio.h>
 #include <linux/uio.h>
 #include <linux/jffs2.h>
 #include <linux/jffs2.h>
 
 
-#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
-		c->free_size -= _x; c->dirty_size += _x; \
-		jeb->free_size -= _x ; jeb->dirty_size += _x; \
-		}while(0)
-#define USED_SPACE(x) do { typeof(x) _x = (x); \
-		c->free_size -= _x; c->used_size += _x; \
-		jeb->free_size -= _x ; jeb->used_size += _x; \
-		}while(0)
-#define WASTED_SPACE(x) do { typeof(x) _x = (x); \
-		c->free_size -= _x; c->wasted_size += _x; \
-		jeb->free_size -= _x ; jeb->wasted_size += _x; \
-		}while(0)
-#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
-		c->free_size -= _x; c->unchecked_size += _x; \
-		jeb->free_size -= _x ; jeb->unchecked_size += _x; \
-		}while(0)
-
 #define BLK_STATE_ALLFF		0
 #define BLK_STATE_ALLFF		0
 #define BLK_STATE_CLEAN		1
 #define BLK_STATE_CLEAN		1
 #define BLK_STATE_PARTDIRTY	2
 #define BLK_STATE_PARTDIRTY	2
@@ -45,6 +28,8 @@
 #define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
 #define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
 #define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
 #define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
 #define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
 #define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
+#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
+#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))
 
 
 /* Summary structures used on flash */
 /* Summary structures used on flash */
 
 
@@ -75,11 +60,28 @@ struct jffs2_sum_dirent_flash
 	uint8_t name[0];	/* dirent name */
 	uint8_t name[0];	/* dirent name */
 } __attribute__((packed));
 } __attribute__((packed));
 
 
+struct jffs2_sum_xattr_flash
+{
+	jint16_t nodetype;	/* == JFFS2_NODETYPE_XATR */
+	jint32_t xid;		/* xattr identifier */
+	jint32_t version;	/* version number */
+	jint32_t offset;	/* offset on jeb */
+	jint32_t totlen;	/* node length */
+} __attribute__((packed));
+
+struct jffs2_sum_xref_flash
+{
+	jint16_t nodetype;	/* == JFFS2_NODETYPE_XREF */
+	jint32_t offset;	/* offset on jeb */
+} __attribute__((packed));
+
 union jffs2_sum_flash
 union jffs2_sum_flash
 {
 {
 	struct jffs2_sum_unknown_flash u;
 	struct jffs2_sum_unknown_flash u;
 	struct jffs2_sum_inode_flash i;
 	struct jffs2_sum_inode_flash i;
 	struct jffs2_sum_dirent_flash d;
 	struct jffs2_sum_dirent_flash d;
+	struct jffs2_sum_xattr_flash x;
+	struct jffs2_sum_xref_flash r;
 };
 };
 
 
 /* Summary structures used in the memory */
 /* Summary structures used in the memory */
@@ -114,11 +116,30 @@ struct jffs2_sum_dirent_mem
 	uint8_t name[0];	/* dirent name */
 	uint8_t name[0];	/* dirent name */
 } __attribute__((packed));
 } __attribute__((packed));
 
 
+struct jffs2_sum_xattr_mem
+{
+	union jffs2_sum_mem *next;
+	jint16_t nodetype;
+	jint32_t xid;
+	jint32_t version;
+	jint32_t offset;
+	jint32_t totlen;
+} __attribute__((packed));
+
+struct jffs2_sum_xref_mem
+{
+	union jffs2_sum_mem *next;
+	jint16_t nodetype;
+	jint32_t offset;
+} __attribute__((packed));
+
 union jffs2_sum_mem
 union jffs2_sum_mem
 {
 {
 	struct jffs2_sum_unknown_mem u;
 	struct jffs2_sum_unknown_mem u;
 	struct jffs2_sum_inode_mem i;
 	struct jffs2_sum_inode_mem i;
 	struct jffs2_sum_dirent_mem d;
 	struct jffs2_sum_dirent_mem d;
+	struct jffs2_sum_xattr_mem x;
+	struct jffs2_sum_xref_mem r;
 };
 };
 
 
 /* Summary related information stored in superblock */
 /* Summary related information stored in superblock */
@@ -159,8 +180,11 @@ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c);
 int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size);
 int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size);
 int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs);
 int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs);
 int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs);
 int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs);
+int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs);
+int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs);
 int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
 int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-			uint32_t ofs, uint32_t *pseudo_random);
+			   struct jffs2_raw_summary *summary, uint32_t sumlen,
+			   uint32_t *pseudo_random);
 
 
 #else				/* SUMMARY DISABLED */
 #else				/* SUMMARY DISABLED */
 
 
@@ -176,7 +200,9 @@ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
 #define jffs2_sum_add_padding_mem(a,b)
 #define jffs2_sum_add_padding_mem(a,b)
 #define jffs2_sum_add_inode_mem(a,b,c)
 #define jffs2_sum_add_inode_mem(a,b,c)
 #define jffs2_sum_add_dirent_mem(a,b,c)
 #define jffs2_sum_add_dirent_mem(a,b,c)
-#define jffs2_sum_scan_sumnode(a,b,c,d) (0)
+#define jffs2_sum_add_xattr_mem(a,b,c)
+#define jffs2_sum_add_xref_mem(a,b,c)
+#define jffs2_sum_scan_sumnode(a,b,c,d,e) (0)
 
 
 #endif /* CONFIG_JFFS2_SUMMARY */
 #endif /* CONFIG_JFFS2_SUMMARY */
 
 

+ 18 - 2
fs/jffs2/super.c

@@ -151,7 +151,10 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
 
 
 	sb->s_op = &jffs2_super_operations;
 	sb->s_op = &jffs2_super_operations;
 	sb->s_flags = flags | MS_NOATIME;
 	sb->s_flags = flags | MS_NOATIME;
-
+	sb->s_xattr = jffs2_xattr_handlers;
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+	sb->s_flags |= MS_POSIXACL;
+#endif
 	ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
 	ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
 
 
 	if (ret) {
 	if (ret) {
@@ -293,6 +296,7 @@ static void jffs2_put_super (struct super_block *sb)
 		kfree(c->blocks);
 		kfree(c->blocks);
 	jffs2_flash_cleanup(c);
 	jffs2_flash_cleanup(c);
 	kfree(c->inocache_list);
 	kfree(c->inocache_list);
+	jffs2_clear_xattr_subsystem(c);
 	if (c->mtd->sync)
 	if (c->mtd->sync)
 		c->mtd->sync(c->mtd);
 		c->mtd->sync(c->mtd);
 
 
@@ -320,6 +324,18 @@ static int __init init_jffs2_fs(void)
 {
 {
 	int ret;
 	int ret;
 
 
+	/* Paranoia checks for on-medium structures. If we ask GCC
+	   to pack them with __attribute__((packed)) then it _also_
+	   assumes that they're not aligned -- so it emits crappy
+	   code on some architectures. Ideally we want an attribute
+	   which means just 'no padding', without the alignment
+	   thing. But GCC doesn't have that -- we have to just
+	   hope the structs are the right sizes, instead. */
+	BUG_ON(sizeof(struct jffs2_unknown_node) != 12);
+	BUG_ON(sizeof(struct jffs2_raw_dirent) != 40);
+	BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
+	BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
+
 	printk(KERN_INFO "JFFS2 version 2.2."
 	printk(KERN_INFO "JFFS2 version 2.2."
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	       " (NAND)"
 	       " (NAND)"
@@ -327,7 +343,7 @@ static int __init init_jffs2_fs(void)
 #ifdef CONFIG_JFFS2_SUMMARY
 #ifdef CONFIG_JFFS2_SUMMARY
 	       " (SUMMARY) "
 	       " (SUMMARY) "
 #endif
 #endif
-	       " (C) 2001-2003 Red Hat, Inc.\n");
+	       " (C) 2001-2006 Red Hat, Inc.\n");
 
 
 	jffs2_inode_cachep = kmem_cache_create("jffs2_i",
 	jffs2_inode_cachep = kmem_cache_create("jffs2_i",
 					     sizeof(struct jffs2_inode_info),
 					     sizeof(struct jffs2_inode_info),

Some files were not shown because too many files changed in this diff