]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge git://git.infradead.org/mtd-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 22 Jun 2009 23:56:22 +0000 (16:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 22 Jun 2009 23:56:22 +0000 (16:56 -0700)
* git://git.infradead.org/mtd-2.6: (63 commits)
  mtd: OneNAND: Allow setting of boundary information when built as module
  jffs2: leaking jffs2_summary in function jffs2_scan_medium
  mtd: nand: Fix memory leak on txx9ndfmc probe failure.
  mtd: orion_nand: use burst reads with double word accesses
  mtd/nand: s3c6400 support for s3c2410 driver
  [MTD] [NAND] S3C2410: Use DIV_ROUND_UP
  [MTD] [NAND] S3C2410: Deal with unaligned lengths in S3C2440 buffer read/write
  [MTD] [NAND] S3C2410: Allow the machine code to get the BBT table from NAND
  [MTD] [NAND] S3C2410: Added a kerneldoc for s3c2410_nand_set
  mtd: physmap_of: Add multiple regions and concatenation support
  mtd: nand: max_retries off by one in mxc_nand
  mtd: nand: s3c2410_nand_setrate(): use correct macros for 2412/2440
  mtd: onenand: add bbt_wait & unlock_all as replaceable for some platform
  mtd: Flex-OneNAND support
  mtd: nand: add OMAP2/OMAP3 NAND driver
  mtd: maps: Blackfin async: fix memory leaks in probe/remove funcs
  mtd: uclinux: mark local stuff static
  mtd: uclinux: do not allow to be built as a module
  mtd: uclinux: allow systems to override map addr/size
  mtd: blackfin NFC: fix hang when using NAND on BF527-EZKITs
  ...

49 files changed:
Documentation/ABI/testing/sysfs-class-mtd [new file with mode: 0644]
Documentation/kernel-parameters.txt
MAINTAINERS
arch/arm/mach-davinci/include/mach/nand.h
arch/arm/plat-s3c/include/plat/nand.h
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/jedec_probe.c
drivers/mtd/devices/m25p80.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/bfin-async-flash.c
drivers/mtd/maps/integrator-flash.c
drivers/mtd/maps/physmap.c
drivers/mtd/maps/physmap_of.c
drivers/mtd/maps/pmcmsp-ramroot.c [deleted file]
drivers/mtd/maps/pxa2xx-flash.c
drivers/mtd/maps/rbtx4939-flash.c
drivers/mtd/maps/sa1100-flash.c
drivers/mtd/maps/uclinux.c
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/bf5xx_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_ecc.c
drivers/mtd/nand/omap2.c [new file with mode: 0644]
drivers/mtd/nand/orion_nand.c
drivers/mtd/nand/plat_nand.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/nand/txx9ndfmc.c
drivers/mtd/onenand/omap2.c
drivers/mtd/onenand/onenand_base.c
drivers/mtd/onenand/onenand_bbt.c
drivers/mtd/onenand/onenand_sim.c
fs/compat_ioctl.c
fs/jffs2/scan.c
include/linux/mtd/nand.h
include/linux/mtd/onenand.h
include/linux/mtd/onenand_regs.h
include/linux/mtd/partitions.h
include/mtd/Kbuild
include/mtd/jffs2-user.h [deleted file]
include/mtd/mtd-abi.h

diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd
new file mode 100644 (file)
index 0000000..4d55a18
--- /dev/null
@@ -0,0 +1,125 @@
+What:          /sys/class/mtd/
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               The mtd/ class subdirectory belongs to the MTD subsystem
+               (MTD core).
+
+What:          /sys/class/mtd/mtdX/
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               The /sys/class/mtd/mtd{0,1,2,3,...} directories correspond
+               to each /dev/mtdX character device.  These may represent
+               physical/simulated flash devices, partitions on a flash
+               device, or concatenated flash devices.  They exist regardless
+               of whether CONFIG_MTD_CHAR is actually enabled.
+
+What:          /sys/class/mtd/mtdXro/
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               These directories provide the corresponding read-only device
+               nodes for /sys/class/mtd/mtdX/ .  They are only created
+               (for the benefit of udev) if CONFIG_MTD_CHAR is enabled.
+
+What:          /sys/class/mtd/mtdX/dev
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               Major and minor numbers of the character device corresponding
+               to this MTD device (in <major>:<minor> format).  This is the
+               read-write device so <minor> will be even.
+
+What:          /sys/class/mtd/mtdXro/dev
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               Major and minor numbers of the character device corresponding
+               to the read-only variant of thie MTD device (in
+               <major>:<minor> format).  In this case <minor> will be odd.
+
+What:          /sys/class/mtd/mtdX/erasesize
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               "Major" erase size for the device.  If numeraseregions is
+               zero, this is the eraseblock size for the entire device.
+               Otherwise, the MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls
+               can be used to determine the actual eraseblock layout.
+
+What:          /sys/class/mtd/mtdX/flags
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               A hexadecimal value representing the device flags, ORed
+               together:
+
+               0x0400: MTD_WRITEABLE - device is writable
+               0x0800: MTD_BIT_WRITEABLE - single bits can be flipped
+               0x1000: MTD_NO_ERASE - no erase necessary
+               0x2000: MTD_POWERUP_LOCK - always locked after reset
+
+What:          /sys/class/mtd/mtdX/name
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               A human-readable ASCII name for the device or partition.
+               This will match the name in /proc/mtd .
+
+What:          /sys/class/mtd/mtdX/numeraseregions
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               For devices that have variable eraseblock sizes, this
+               provides the total number of erase regions.  Otherwise,
+               it will read back as zero.
+
+What:          /sys/class/mtd/mtdX/oobsize
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               Number of OOB bytes per page.
+
+What:          /sys/class/mtd/mtdX/size
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               Total size of the device/partition, in bytes.
+
+What:          /sys/class/mtd/mtdX/type
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               One of the following ASCII strings, representing the device
+               type:
+
+               absent, ram, rom, nor, nand, dataflash, ubi, unknown
+
+What:          /sys/class/mtd/mtdX/writesize
+Date:          April 2009
+KernelVersion: 2.6.29
+Contact:       linux-mtd@lists.infradead.org
+Description:
+               Minimal writable flash unit size.  This will always be
+               a positive integer.
+
+               In the case of NOR flash it is 1 (even though individual
+               bits can be cleared).
+
+               In the case of NAND flash it is one NAND page (or a
+               half page, or a quarter page).
+
+               In the case of ECC NOR, it is the ECC block size.
index 5092a2be83c57b5ebb3d1de05493b5f7a9c36ce4..54ebf100e4e02301e8f2f88a0b98ddc85d653a85 100644 (file)
@@ -1431,6 +1431,16 @@ and is between 256 and 4096 characters. It is defined in the file
        mtdparts=       [MTD]
                        See drivers/mtd/cmdlinepart.c.
 
+       onenand.bdry=   [HW,MTD] Flex-OneNAND Boundary Configuration
+
+                       Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock]
+
+                       boundary - index of last SLC block on Flex-OneNAND.
+                                  The remaining blocks are configured as MLC blocks.
+                       lock     - Configure if Flex-OneNAND boundary should be locked.
+                                  Once locked, the boundary cannot be changed.
+                                  1 indicates lock status, 0 indicates unlock status.
+
        mtdset=         [ARM]
                        ARM/S3C2412 JIVE boot control
 
index cf5a46ef6b3f4ae57562cf8bf6de23d3623386ad..02f6f78b561f4bce68789776d5527ab5dbabcc22 100644 (file)
@@ -3252,7 +3252,6 @@ W:        http://www.linux-mtd.infradead.org/doc/jffs2.html
 S:     Maintained
 F:     fs/jffs2/
 F:     include/linux/jffs2.h
-F:     include/mtd/jffs2-user.h
 
 JOURNALLING LAYER FOR BLOCK DEVICES (JBD)
 P:     Stephen Tweedie
index aa482841270befd1082d3ae3931b5214d4120139..b520c4b5678a2480b8e454b25b7f3b9034c84dae 100644 (file)
@@ -68,10 +68,14 @@ struct davinci_nand_pdata {         /* platform_data */
 
        /* none  == NAND_ECC_NONE (strongly *not* advised!!)
         * soft  == NAND_ECC_SOFT
-        * 1-bit == NAND_ECC_HW
-        * 4-bit == NAND_ECC_HW_SYNDROME (not on all chips)
+        * else  == NAND_ECC_HW, according to ecc_bits
+        *
+        * All DaVinci-family chips support 1-bit hardware ECC.
+        * Newer ones also support 4-bit ECC, but are awkward
+        * using it with large page chips.
         */
        nand_ecc_modes_t        ecc_mode;
+       u8                      ecc_bits;
 
        /* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */
        unsigned                options;
index f4dcd14af05957cb197a095377c325c5d01b1362..18f958801e64c16957d26ac9a1a41da39bf72e9e 100644 (file)
  * published by the Free Software Foundation.
 */
 
-/* struct s3c2410_nand_set
+/**
+ * struct s3c2410_nand_set - define a set of one or more nand chips
+ * @disable_ecc:       Entirely disable ECC - Dangerous
+ * @flash_bbt:                 Openmoko u-boot can create a Bad Block Table
+ *                     Setting this flag will allow the kernel to
+ *                     look for it at boot time and also skip the NAND
+ *                     scan.
+ * @nr_chips:          Number of chips in this set
+ * @nr_partitions:     Number of partitions pointed to by @partitions
+ * @name:              Name of set (optional)
+ * @nr_map:            Map for low-layer logical to physical chip numbers (option)
+ * @partitions:                The mtd partition list
  *
- * define an set of one or more nand chips registered with an unique mtd
- *
- * nr_chips     = number of chips in this set
- * nr_partitions = number of partitions pointed to be partitoons (or zero)
- * name                 = name of set (optional)
- * nr_map       = map for low-layer logical to physical chip numbers (option)
- * partitions   = mtd partition list
-*/
-
+ * define a set of one or more nand chips registered with an unique mtd. Also
+ * allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger
+ * a warning at boot time.
+ */
 struct s3c2410_nand_set {
-       unsigned int            disable_ecc : 1;
+       unsigned int            disable_ecc:1;
+       unsigned int            flash_bbt:1;
 
        int                     nr_chips;
        int                     nr_partitions;
@@ -39,7 +46,7 @@ struct s3c2410_platform_nand {
        int     twrph0; /* active time for nWE/nOE */
        int     twrph1; /* time for release CLE/ALE from nWE/nOE inactive */
 
-       unsigned int    ignore_unset_ecc : 1;
+       unsigned int    ignore_unset_ecc:1;
 
        int                     nr_sets;
        struct s3c2410_nand_set *sets;
index c240454fd113d757794141a160eaaac60015c036..8664feebc93ba3bf184857dfb42f7cb7f51b9a4a 100644 (file)
@@ -46,6 +46,7 @@
 #define MANUFACTURER_INTEL     0x0089
 #define I82802AB       0x00ad
 #define I82802AC       0x00ac
+#define PF38F4476      0x881c
 #define MANUFACTURER_ST         0x0020
 #define M50LPW080       0x002F
 #define M50FLW080A     0x0080
@@ -315,10 +316,20 @@ static struct cfi_fixup fixup_table[] = {
        { 0, 0, NULL, NULL }
 };
 
+static void cfi_fixup_major_minor(struct cfi_private *cfi,
+                                               struct cfi_pri_intelext *extp)
+{
+       if (cfi->mfr == MANUFACTURER_INTEL &&
+                       cfi->id == PF38F4476 && extp->MinorVersion == '3')
+               extp->MinorVersion = '1';
+}
+
 static inline struct cfi_pri_intelext *
 read_pri_intelext(struct map_info *map, __u16 adr)
 {
+       struct cfi_private *cfi = map->fldrv_priv;
        struct cfi_pri_intelext *extp;
+       unsigned int extra_size = 0;
        unsigned int extp_size = sizeof(*extp);
 
  again:
@@ -326,6 +337,8 @@ read_pri_intelext(struct map_info *map, __u16 adr)
        if (!extp)
                return NULL;
 
+       cfi_fixup_major_minor(cfi, extp);
+
        if (extp->MajorVersion != '1' ||
            (extp->MinorVersion < '0' || extp->MinorVersion > '5')) {
                printk(KERN_ERR "  Unknown Intel/Sharp Extended Query "
@@ -340,19 +353,24 @@ read_pri_intelext(struct map_info *map, __u16 adr)
        extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask);
        extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr);
 
-       if (extp->MajorVersion == '1' && extp->MinorVersion >= '3') {
-               unsigned int extra_size = 0;
-               int nb_parts, i;
+       if (extp->MinorVersion >= '0') {
+               extra_size = 0;
 
                /* Protection Register info */
                extra_size += (extp->NumProtectionFields - 1) *
                              sizeof(struct cfi_intelext_otpinfo);
+       }
 
+       if (extp->MinorVersion >= '1') {
                /* Burst Read info */
                extra_size += 2;
                if (extp_size < sizeof(*extp) + extra_size)
                        goto need_more;
-               extra_size += extp->extra[extra_size-1];
+               extra_size += extp->extra[extra_size - 1];
+       }
+
+       if (extp->MinorVersion >= '3') {
+               int nb_parts, i;
 
                /* Number of hardware-partitions */
                extra_size += 1;
index e824b9b9b05619b18a9348ffa74c8586b865842a..ccc4cfc7e4b5efb415d6887216c137d863ae1cad 100644 (file)
 #define SST39LF040     0x00D7
 #define SST39SF010A    0x00B5
 #define SST39SF020A    0x00B6
+#define SST39SF040     0x00B7
 #define SST49LF004B    0x0060
 #define SST49LF040B    0x0050
 #define SST49LF008A    0x005a
@@ -1391,6 +1392,18 @@ static const struct amd_flash_info jedec_table[] = {
                .regions        = {
                        ERASEINFO(0x01000,64),
                }
+       }, {
+               .mfr_id         = MANUFACTURER_SST,
+               .dev_id         = SST39SF040,
+               .name           = "SST 39SF040",
+               .devtypes       = CFI_DEVICETYPE_X8,
+               .uaddr          = MTD_UADDR_0x5555_0x2AAA,
+               .dev_size       = SIZE_512KiB,
+               .cmd_set        = P_ID_AMD_STD,
+               .nr_regions     = 1,
+               .regions        = {
+                       ERASEINFO(0x01000,128),
+               }
        }, {
                .mfr_id         = MANUFACTURER_SST,
                .dev_id         = SST49LF040B,
index cc6369ea67dd22051eccddcf88f90ee19096c5d5..59c46126a5ce3ddb63bf4a02aa52c7e621b1b80a 100644 (file)
@@ -500,6 +500,9 @@ static struct flash_info __devinitdata m25p_data [] = {
        { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, },
        { "at26df321",  0x1f4701, 0, 64 * 1024, 64, SECT_4K, },
 
+       /* Macronix */
+       { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, },
+
        /* Spansion -- single (large) sector size only, at least
         * for the chips listed here (without boot sectors).
         */
@@ -528,6 +531,7 @@ static struct flash_info __devinitdata m25p_data [] = {
        { "m25p64",  0x202017,  0, 64 * 1024, 128, },
        { "m25p128", 0x202018, 0, 256 * 1024, 64, },
 
+       { "m45pe10", 0x204011,  0, 64 * 1024, 2, },
        { "m45pe80", 0x204014,  0, 64 * 1024, 16, },
        { "m45pe16", 0x204015,  0, 64 * 1024, 32, },
 
index 82923bd2d9c5444166c53552a409f27ebb646e50..0b98654d8eed27831b591c3ec2575771c44166d8 100644 (file)
@@ -105,15 +105,6 @@ config MSP_FLASH_MAP_LIMIT
        default "0x02000000"
        depends on MSP_FLASH_MAP_LIMIT_32M
 
-config MTD_PMC_MSP_RAMROOT
-       tristate "Embedded RAM block device for root on PMC-Sierra MSP"
-       depends on PMC_MSP_EMBEDDED_ROOTFS && \
-                       (MTD_BLOCK || MTD_BLOCK_RO) && \
-                       MTD_RAM
-       help
-         This provides support for the embedded root file system
-          on PMC MSP devices.  This memory is mapped as a MTD block device.
-
 config MTD_SUN_UFLASH
        tristate "Sun Microsystems userflash support"
        depends on SPARC && MTD_CFI && PCI
@@ -270,7 +261,7 @@ config MTD_ALCHEMY
 
 config MTD_DILNETPC
        tristate "CFI Flash device mapped on DIL/Net PC"
-       depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT
+       depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
        help
          MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP".
          For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm>
@@ -501,7 +492,7 @@ config MTD_BFIN_ASYNC
          If compiled as a module, it will be called bfin-async-flash.
 
 config MTD_UCLINUX
-       tristate "Generic uClinux RAM/ROM filesystem support"
+       bool "Generic uClinux RAM/ROM filesystem support"
        depends on MTD_PARTITIONS && MTD_RAM && !MMU
        help
          Map driver to support image based filesystems for uClinux.
index 2dbc1bec848808559f4bc52995eda5ee06fb8dad..8bae7f9850c0b1acbb2e782734f01bd612f8a985 100644 (file)
@@ -25,7 +25,6 @@ obj-$(CONFIG_MTD_OCTAGON)     += octagon-5066.o
 obj-$(CONFIG_MTD_PHYSMAP)      += physmap.o
 obj-$(CONFIG_MTD_PHYSMAP_OF)   += physmap_of.o
 obj-$(CONFIG_MTD_PMC_MSP_EVM)   += pmcmsp-flash.o
-obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o
 obj-$(CONFIG_MTD_PCMCIA)       += pcmciamtd.o
 obj-$(CONFIG_MTD_RPXLITE)      += rpxlite.o
 obj-$(CONFIG_MTD_TQM8XXL)      += tqm8xxl.o
index 576611f605db1df5063775a677d3cc499cb010e7..365c77b1b871d5d81d939989e406cfa3940fb4c4 100644 (file)
@@ -40,6 +40,9 @@ struct async_state {
        uint32_t flash_ambctl0, flash_ambctl1;
        uint32_t save_ambctl0, save_ambctl1;
        unsigned long irq_flags;
+#ifdef CONFIG_MTD_PARTITIONS
+       struct mtd_partition *parts;
+#endif
 };
 
 static void switch_to_flash(struct async_state *state)
@@ -170,6 +173,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
        if (ret > 0) {
                pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n");
                add_mtd_partitions(state->mtd, pdata->parts, ret);
+               state->parts = pdata->parts;
 
        } else if (pdata->nr_parts) {
                pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n");
@@ -193,6 +197,7 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev)
        gpio_free(state->enet_flash_pin);
 #ifdef CONFIG_MTD_PARTITIONS
        del_mtd_partitions(state->mtd);
+       kfree(state->parts);
 #endif
        map_destroy(state->mtd);
        kfree(state);
index c9681a339a594b2c10913027425d931c655e7bb4..b08a798ee254be0ed501758f0300df314c18078b 100644 (file)
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
+#include <linux/mtd/concat.h>
 
 #include <asm/mach/flash.h>
 #include <mach/hardware.h>
 #include <asm/system.h>
 
-#ifdef CONFIG_ARCH_P720T
-#define FLASH_BASE             (0x04000000)
-#define FLASH_SIZE             (64*1024*1024)
-#endif
+#define SUBDEV_NAME_SIZE       (BUS_ID_SIZE + 2)
 
-struct armflash_info {
+struct armflash_subdev_info {
+       char                    name[SUBDEV_NAME_SIZE];
+       struct mtd_info         *mtd;
+       struct map_info         map;
        struct flash_platform_data *plat;
+};
+
+struct armflash_info {
        struct resource         *res;
        struct mtd_partition    *parts;
        struct mtd_info         *mtd;
-       struct map_info         map;
+       int                     nr_subdev;
+       struct armflash_subdev_info subdev[0];
 };
 
 static void armflash_set_vpp(struct map_info *map, int on)
 {
-       struct armflash_info *info = container_of(map, struct armflash_info, map);
+       struct armflash_subdev_info *info =
+               container_of(map, struct armflash_subdev_info, map);
 
        if (info->plat && info->plat->set_vpp)
                info->plat->set_vpp(on);
@@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on)
 
 static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
 
-static int armflash_probe(struct platform_device *dev)
+static int armflash_subdev_probe(struct armflash_subdev_info *subdev,
+                                struct resource *res)
 {
-       struct flash_platform_data *plat = dev->dev.platform_data;
-       struct resource *res = dev->resource;
-       unsigned int size = res->end - res->start + 1;
-       struct armflash_info *info;
-       int err;
+       struct flash_platform_data *plat = subdev->plat;
+       resource_size_t size = res->end - res->start + 1;
        void __iomem *base;
+       int err = 0;
 
-       info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL);
-       if (!info) {
-               err = -ENOMEM;
-               goto out;
-       }
-
-       info->plat = plat;
-       if (plat && plat->init) {
-               err = plat->init();
-               if (err)
-                       goto no_resource;
-       }
-
-       info->res = request_mem_region(res->start, size, "armflash");
-       if (!info->res) {
+       if (!request_mem_region(res->start, size, subdev->name)) {
                err = -EBUSY;
-               goto no_resource;
+               goto out;
        }
 
        base = ioremap(res->start, size);
@@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev)
        /*
         * look for CFI based flash parts fitted to this board
         */
-       info->map.size          = size;
-       info->map.bankwidth     = plat->width;
-       info->map.phys          = res->start;
-       info->map.virt          = base;
-       info->map.name          = dev_name(&dev->dev);
-       info->map.set_vpp       = armflash_set_vpp;
+       subdev->map.size        = size;
+       subdev->map.bankwidth   = plat->width;
+       subdev->map.phys        = res->start;
+       subdev->map.virt        = base;
+       subdev->map.name        = subdev->name;
+       subdev->map.set_vpp     = armflash_set_vpp;
 
-       simple_map_init(&info->map);
+       simple_map_init(&subdev->map);
 
        /*
         * Also, the CFI layer automatically works out what size
         * of chips we have, and does the necessary identification
         * for us automatically.
         */
-       info->mtd = do_map_probe(plat->map_name, &info->map);
-       if (!info->mtd) {
+       subdev->mtd = do_map_probe(plat->map_name, &subdev->map);
+       if (!subdev->mtd) {
                err = -ENXIO;
                goto no_device;
        }
 
-       info->mtd->owner = THIS_MODULE;
+       subdev->mtd->owner = THIS_MODULE;
+
+       /* Successful? */
+       if (err == 0)
+               return err;
+
+       if (subdev->mtd)
+               map_destroy(subdev->mtd);
+ no_device:
+       iounmap(base);
+ no_mem:
+       release_mem_region(res->start, size);
+ out:
+       return err;
+}
+
+static void armflash_subdev_remove(struct armflash_subdev_info *subdev)
+{
+       if (subdev->mtd)
+               map_destroy(subdev->mtd);
+       if (subdev->map.virt)
+               iounmap(subdev->map.virt);
+       release_mem_region(subdev->map.phys, subdev->map.size);
+}
+
+static int armflash_probe(struct platform_device *dev)
+{
+       struct flash_platform_data *plat = dev->dev.platform_data;
+       unsigned int size;
+       struct armflash_info *info;
+       int i, nr, err;
+
+       /* Count the number of devices */
+       for (nr = 0; ; nr++)
+               if (!platform_get_resource(dev, IORESOURCE_MEM, nr))
+                       break;
+       if (nr == 0) {
+               err = -ENODEV;
+               goto out;
+       }
+
+       size = sizeof(struct armflash_info) +
+               sizeof(struct armflash_subdev_info) * nr;
+       info = kzalloc(size, GFP_KERNEL);
+       if (!info) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       if (plat && plat->init) {
+               err = plat->init();
+               if (err)
+                       goto no_resource;
+       }
+
+       for (i = 0; i < nr; i++) {
+               struct armflash_subdev_info *subdev = &info->subdev[i];
+               struct resource *res;
+
+               res = platform_get_resource(dev, IORESOURCE_MEM, i);
+               if (!res)
+                       break;
+
+               if (nr == 1)
+                       /* No MTD concatenation, just use the default name */
+                       snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s",
+                                dev_name(&dev->dev));
+               else
+                       snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d",
+                                dev_name(&dev->dev), i);
+               subdev->plat = plat;
+
+               err = armflash_subdev_probe(subdev, res);
+               if (err)
+                       break;
+       }
+       info->nr_subdev = i;
+
+       if (err)
+               goto subdev_err;
+
+       if (info->nr_subdev == 1)
+               info->mtd = info->subdev[0].mtd;
+       else if (info->nr_subdev > 1) {
+#ifdef CONFIG_MTD_CONCAT
+               struct mtd_info *cdev[info->nr_subdev];
+
+               /*
+                * We detected multiple devices.  Concatenate them together.
+                */
+               for (i = 0; i < info->nr_subdev; i++)
+                       cdev[i] = info->subdev[i].mtd;
+
+               info->mtd = mtd_concat_create(cdev, info->nr_subdev,
+                                             dev_name(&dev->dev));
+               if (info->mtd == NULL)
+                       err = -ENXIO;
+#else
+               printk(KERN_ERR "armflash: multiple devices found but "
+                      "MTD concat support disabled.\n");
+               err = -ENXIO;
+#endif
+       }
+
+       if (err < 0)
+               goto cleanup;
 
        err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
        if (err > 0) {
@@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev)
                               "mtd partition registration failed: %d\n", err);
        }
 
-       if (err == 0)
+       if (err == 0) {
                platform_set_drvdata(dev, info);
+               return err;
+       }
 
        /*
-        * If we got an error, free all resources.
+        * We got an error, free all resources.
         */
-       if (err < 0) {
-               if (info->mtd) {
-                       del_mtd_partitions(info->mtd);
-                       map_destroy(info->mtd);
-               }
-               kfree(info->parts);
-
- no_device:
-               iounmap(base);
- no_mem:
-               release_mem_region(res->start, size);
- no_resource:
-               if (plat && plat->exit)
-                       plat->exit();
-               kfree(info);
+ cleanup:
+       if (info->mtd) {
+               del_mtd_partitions(info->mtd);
+#ifdef CONFIG_MTD_CONCAT
+               if (info->mtd != info->subdev[0].mtd)
+                       mtd_concat_destroy(info->mtd);
+#endif
        }
+       kfree(info->parts);
+ subdev_err:
+       for (i = info->nr_subdev - 1; i >= 0; i--)
+               armflash_subdev_remove(&info->subdev[i]);
+ no_resource:
+       if (plat && plat->exit)
+               plat->exit();
+       kfree(info);
  out:
        return err;
 }
@@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev)
 static int armflash_remove(struct platform_device *dev)
 {
        struct armflash_info *info = platform_get_drvdata(dev);
+       struct flash_platform_data *plat = dev->dev.platform_data;
+       int i;
 
        platform_set_drvdata(dev, NULL);
 
        if (info) {
                if (info->mtd) {
                        del_mtd_partitions(info->mtd);
-                       map_destroy(info->mtd);
+#ifdef CONFIG_MTD_CONCAT
+                       if (info->mtd != info->subdev[0].mtd)
+                               mtd_concat_destroy(info->mtd);
+#endif
                }
                kfree(info->parts);
 
-               iounmap(info->map.virt);
-               release_resource(info->res);
-               kfree(info->res);
+               for (i = info->nr_subdev - 1; i >= 0; i--)
+                       armflash_subdev_remove(&info->subdev[i]);
 
-               if (info->plat && info->plat->exit)
-                       info->plat->exit();
+               if (plat && plat->exit)
+                       plat->exit();
 
                kfree(info);
        }
index 29a9011573522abbe7d8b021ddec46182dcfb2d2..380648e9051adc1b2d25eb295147859b40f2b140 100644 (file)
@@ -195,42 +195,6 @@ err_out:
 }
 
 #ifdef CONFIG_PM
-static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state)
-{
-       struct physmap_flash_info *info = platform_get_drvdata(dev);
-       int ret = 0;
-       int i;
-
-       for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
-               if (info->mtd[i]->suspend) {
-                       ret = info->mtd[i]->suspend(info->mtd[i]);
-                       if (ret)
-                               goto fail;
-               }
-
-       return 0;
-fail:
-       for (--i; i >= 0; --i)
-               if (info->mtd[i]->suspend) {
-                       BUG_ON(!info->mtd[i]->resume);
-                       info->mtd[i]->resume(info->mtd[i]);
-               }
-
-       return ret;
-}
-
-static int physmap_flash_resume(struct platform_device *dev)
-{
-       struct physmap_flash_info *info = platform_get_drvdata(dev);
-       int i;
-
-       for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
-               if (info->mtd[i]->resume)
-                       info->mtd[i]->resume(info->mtd[i]);
-
-       return 0;
-}
-
 static void physmap_flash_shutdown(struct platform_device *dev)
 {
        struct physmap_flash_info *info = platform_get_drvdata(dev);
@@ -242,16 +206,12 @@ static void physmap_flash_shutdown(struct platform_device *dev)
                                info->mtd[i]->resume(info->mtd[i]);
 }
 #else
-#define physmap_flash_suspend NULL
-#define physmap_flash_resume NULL
 #define physmap_flash_shutdown NULL
 #endif
 
 static struct platform_driver physmap_flash_driver = {
        .probe          = physmap_flash_probe,
        .remove         = physmap_flash_remove,
-       .suspend        = physmap_flash_suspend,
-       .resume         = physmap_flash_resume,
        .shutdown       = physmap_flash_shutdown,
        .driver         = {
                .name   = "physmap-flash",
index c83a60fada530e1a73f812bef5c44d18e7432259..39d357b2eb47fdb24a0b7889cee4adeb46ee3171 100644 (file)
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
+#include <linux/mtd/concat.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
 
+struct of_flash_list {
+       struct mtd_info *mtd;
+       struct map_info map;
+       struct resource *res;
+};
+
 struct of_flash {
-       struct mtd_info         *mtd;
-       struct map_info         map;
-       struct resource         *res;
+       struct mtd_info         *cmtd;
 #ifdef CONFIG_MTD_PARTITIONS
        struct mtd_partition    *parts;
 #endif
+       int list_size; /* number of elements in of_flash_list */
+       struct of_flash_list    list[0];
 };
 
 #ifdef CONFIG_MTD_PARTITIONS
@@ -88,30 +95,44 @@ static int parse_obsolete_partitions(struct of_device *dev,
 static int of_flash_remove(struct of_device *dev)
 {
        struct of_flash *info;
+       int i;
 
        info = dev_get_drvdata(&dev->dev);
        if (!info)
                return 0;
        dev_set_drvdata(&dev->dev, NULL);
 
-       if (info->mtd) {
+#ifdef CONFIG_MTD_CONCAT
+       if (info->cmtd != info->list[0].mtd) {
+               del_mtd_device(info->cmtd);
+               mtd_concat_destroy(info->cmtd);
+       }
+#endif
+
+       if (info->cmtd) {
                if (OF_FLASH_PARTS(info)) {
-                       del_mtd_partitions(info->mtd);
+                       del_mtd_partitions(info->cmtd);
                        kfree(OF_FLASH_PARTS(info));
                } else {
-                       del_mtd_device(info->mtd);
+                       del_mtd_device(info->cmtd);
                }
-               map_destroy(info->mtd);
        }
 
-       if (info->map.virt)
-               iounmap(info->map.virt);
+       for (i = 0; i < info->list_size; i++) {
+               if (info->list[i].mtd)
+                       map_destroy(info->list[i].mtd);
 
-       if (info->res) {
-               release_resource(info->res);
-               kfree(info->res);
+               if (info->list[i].map.virt)
+                       iounmap(info->list[i].map.virt);
+
+               if (info->list[i].res) {
+                       release_resource(info->list[i].res);
+                       kfree(info->list[i].res);
+               }
        }
 
+       kfree(info);
+
        return 0;
 }
 
@@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev,
        const char *probe_type = match->data;
        const u32 *width;
        int err;
-
-       err = -ENXIO;
-       if (of_address_to_resource(dp, 0, &res)) {
-               dev_err(&dev->dev, "Can't get IO address from device tree\n");
+       int i;
+       int count;
+       const u32 *p;
+       int reg_tuple_size;
+       struct mtd_info **mtd_list = NULL;
+
+       reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
+
+       /*
+        * Get number of "reg" tuples. Scan for MTD devices on area's
+        * described by each "reg" region. This makes it possible (including
+        * the concat support) to support the Intel P30 48F4400 chips which
+        * consists internally of 2 non-identical NOR chips on one die.
+        */
+       p = of_get_property(dp, "reg", &count);
+       if (count % reg_tuple_size != 0) {
+               dev_err(&dev->dev, "Malformed reg property on %s\n",
+                               dev->node->full_name);
+               err = -EINVAL;
                goto err_out;
        }
-
-               dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
-               (unsigned long long)res.start, (unsigned long long)res.end);
+       count /= reg_tuple_size;
 
        err = -ENOMEM;
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       info = kzalloc(sizeof(struct of_flash) +
+                      sizeof(struct of_flash_list) * count, GFP_KERNEL);
+       if (!info)
+               goto err_out;
+
+       mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
        if (!info)
                goto err_out;
 
        dev_set_drvdata(&dev->dev, info);
 
-       err = -EBUSY;
-       info->res = request_mem_region(res.start, res.end - res.start + 1,
-                                      dev_name(&dev->dev));
-       if (!info->res)
-               goto err_out;
+       for (i = 0; i < count; i++) {
+               err = -ENXIO;
+               if (of_address_to_resource(dp, i, &res)) {
+                       dev_err(&dev->dev, "Can't get IO address from device"
+                               " tree\n");
+                       goto err_out;
+               }
 
-       err = -ENXIO;
-       width = of_get_property(dp, "bank-width", NULL);
-       if (!width) {
-               dev_err(&dev->dev, "Can't get bank width from device tree\n");
-               goto err_out;
-       }
+               dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
+                       (unsigned long long)res.start,
+                       (unsigned long long)res.end);
+
+               err = -EBUSY;
+               info->list[i].res = request_mem_region(res.start, res.end -
+                                                      res.start + 1,
+                                                      dev_name(&dev->dev));
+               if (!info->list[i].res)
+                       goto err_out;
+
+               err = -ENXIO;
+               width = of_get_property(dp, "bank-width", NULL);
+               if (!width) {
+                       dev_err(&dev->dev, "Can't get bank width from device"
+                               " tree\n");
+                       goto err_out;
+               }
 
-       info->map.name = dev_name(&dev->dev);
-       info->map.phys = res.start;
-       info->map.size = res.end - res.start + 1;
-       info->map.bankwidth = *width;
+               info->list[i].map.name = dev_name(&dev->dev);
+               info->list[i].map.phys = res.start;
+               info->list[i].map.size = res.end - res.start + 1;
+               info->list[i].map.bankwidth = *width;
+
+               err = -ENOMEM;
+               info->list[i].map.virt = ioremap(info->list[i].map.phys,
+                                                info->list[i].map.size);
+               if (!info->list[i].map.virt) {
+                       dev_err(&dev->dev, "Failed to ioremap() flash"
+                               " region\n");
+                       goto err_out;
+               }
 
-       err = -ENOMEM;
-       info->map.virt = ioremap(info->map.phys, info->map.size);
-       if (!info->map.virt) {
-               dev_err(&dev->dev, "Failed to ioremap() flash region\n");
-               goto err_out;
-       }
+               simple_map_init(&info->list[i].map);
 
-       simple_map_init(&info->map);
+               if (probe_type) {
+                       info->list[i].mtd = do_map_probe(probe_type,
+                                                        &info->list[i].map);
+               } else {
+                       info->list[i].mtd = obsolete_probe(dev,
+                                                          &info->list[i].map);
+               }
+               mtd_list[i] = info->list[i].mtd;
 
-       if (probe_type)
-               info->mtd = do_map_probe(probe_type, &info->map);
-       else
-               info->mtd = obsolete_probe(dev, &info->map);
+               err = -ENXIO;
+               if (!info->list[i].mtd) {
+                       dev_err(&dev->dev, "do_map_probe() failed\n");
+                       goto err_out;
+               } else {
+                       info->list_size++;
+               }
+               info->list[i].mtd->owner = THIS_MODULE;
+               info->list[i].mtd->dev.parent = &dev->dev;
+       }
 
-       err = -ENXIO;
-       if (!info->mtd) {
-               dev_err(&dev->dev, "do_map_probe() failed\n");
-               goto err_out;
+       err = 0;
+       if (info->list_size == 1) {
+               info->cmtd = info->list[0].mtd;
+       } else if (info->list_size > 1) {
+               /*
+                * We detected multiple devices. Concatenate them together.
+                */
+#ifdef CONFIG_MTD_CONCAT
+               info->cmtd = mtd_concat_create(mtd_list, info->list_size,
+                                              dev_name(&dev->dev));
+               if (info->cmtd == NULL)
+                       err = -ENXIO;
+#else
+               printk(KERN_ERR "physmap_of: multiple devices "
+                      "found but MTD concat support disabled.\n");
+               err = -ENXIO;
+#endif
        }
-       info->mtd->owner = THIS_MODULE;
-       info->mtd->dev.parent = &dev->dev;
+       if (err)
+               goto err_out;
 
 #ifdef CONFIG_MTD_PARTITIONS
        /* First look for RedBoot table or partitions on the command
         * line, these take precedence over device tree information */
-       err = parse_mtd_partitions(info->mtd, part_probe_types,
-                                  &info->parts, 0);
+       err = parse_mtd_partitions(info->cmtd, part_probe_types,
+                                  &info->parts, 0);
        if (err < 0)
                return err;
 
@@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev,
        }
 
        if (err > 0)
-               add_mtd_partitions(info->mtd, info->parts, err);
+               add_mtd_partitions(info->cmtd, info->parts, err);
        else
 #endif
-               add_mtd_device(info->mtd);
+               add_mtd_device(info->cmtd);
+
+       kfree(mtd_list);
 
        return 0;
 
 err_out:
+       kfree(mtd_list);
        of_flash_remove(dev);
+
        return err;
 }
 
diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c
deleted file mode 100644 (file)
index 30de5c0..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Mapping of the rootfs in a physical region of memory
- *
- * Copyright (C) 2005-2007 PMC-Sierra Inc.
- * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
- *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
- *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
- *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
- *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
- *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *  You should have received a copy of the  GNU General Public License along
- *  with this program; if not, write  to the Free Software Foundation, Inc.,
- *  675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/root_dev.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-
-#include <asm/io.h>
-
-#include <msp_prom.h>
-
-static struct mtd_info *rr_mtd;
-
-struct map_info rr_map = {
-       .name = "ramroot",
-       .bankwidth = 4,
-};
-
-static int __init init_rrmap(void)
-{
-       void *ramroot_start;
-       unsigned long ramroot_size;
-
-       /* Check for supported rootfs types */
-       if (get_ramroot(&ramroot_start, &ramroot_size)) {
-               rr_map.phys = CPHYSADDR(ramroot_start);
-               rr_map.size = ramroot_size;
-
-               printk(KERN_NOTICE
-                       "PMC embedded root device: 0x%08lx @ 0x%08lx\n",
-                       rr_map.size, (unsigned long)rr_map.phys);
-       } else {
-               printk(KERN_ERR
-                       "init_rrmap: no supported embedded rootfs detected!\n");
-               return -ENXIO;
-       }
-
-       /* Map rootfs to I/O space for block device driver */
-       rr_map.virt = ioremap(rr_map.phys, rr_map.size);
-       if (!rr_map.virt) {
-               printk(KERN_ERR "Failed to ioremap\n");
-               return -EIO;
-       }
-
-       simple_map_init(&rr_map);
-
-       rr_mtd = do_map_probe("map_ram", &rr_map);
-       if (rr_mtd) {
-               rr_mtd->owner = THIS_MODULE;
-
-               add_mtd_device(rr_mtd);
-
-               return 0;
-       }
-
-       iounmap(rr_map.virt);
-       return -ENXIO;
-}
-
-static void __exit cleanup_rrmap(void)
-{
-       del_mtd_device(rr_mtd);
-       map_destroy(rr_mtd);
-
-       iounmap(rr_map.virt);
-       rr_map.virt = NULL;
-}
-
-MODULE_AUTHOR("PMC-Sierra, Inc");
-MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem");
-MODULE_LICENSE("GPL");
-
-module_init(init_rrmap);
-module_exit(cleanup_rrmap);
index 572d32fdf38a0d8b421b094e931978e54b633745..643aa06b599e8f9733d0c87072c21f530d5f20d8 100644 (file)
@@ -140,24 +140,6 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev)
 }
 
 #ifdef CONFIG_PM
-static int pxa2xx_flash_suspend(struct platform_device *dev, pm_message_t state)
-{
-       struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
-       int ret = 0;
-
-       if (info->mtd && info->mtd->suspend)
-               ret = info->mtd->suspend(info->mtd);
-       return ret;
-}
-
-static int pxa2xx_flash_resume(struct platform_device *dev)
-{
-       struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
-
-       if (info->mtd && info->mtd->resume)
-               info->mtd->resume(info->mtd);
-       return 0;
-}
 static void pxa2xx_flash_shutdown(struct platform_device *dev)
 {
        struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
@@ -166,8 +148,6 @@ static void pxa2xx_flash_shutdown(struct platform_device *dev)
                info->mtd->resume(info->mtd);
 }
 #else
-#define pxa2xx_flash_suspend NULL
-#define pxa2xx_flash_resume NULL
 #define pxa2xx_flash_shutdown NULL
 #endif
 
@@ -178,8 +158,6 @@ static struct platform_driver pxa2xx_flash_driver = {
        },
        .probe          = pxa2xx_flash_probe,
        .remove         = __devexit_p(pxa2xx_flash_remove),
-       .suspend        = pxa2xx_flash_suspend,
-       .resume         = pxa2xx_flash_resume,
        .shutdown       = pxa2xx_flash_shutdown,
 };
 
index d39f0adac846faa194dbc046440041a89409ec01..83ed64512c5e6f076192abf097cb9aacde1c2ab6 100644 (file)
@@ -145,25 +145,6 @@ err_out:
 }
 
 #ifdef CONFIG_PM
-static int rbtx4939_flash_suspend(struct platform_device *dev,
-                                 pm_message_t state)
-{
-       struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
-
-       if (info->mtd->suspend)
-               return info->mtd->suspend(info->mtd);
-       return 0;
-}
-
-static int rbtx4939_flash_resume(struct platform_device *dev)
-{
-       struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
-
-       if (info->mtd->resume)
-               info->mtd->resume(info->mtd);
-       return 0;
-}
-
 static void rbtx4939_flash_shutdown(struct platform_device *dev)
 {
        struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
@@ -173,16 +154,12 @@ static void rbtx4939_flash_shutdown(struct platform_device *dev)
                        info->mtd->resume(info->mtd);
 }
 #else
-#define rbtx4939_flash_suspend NULL
-#define rbtx4939_flash_resume NULL
 #define rbtx4939_flash_shutdown NULL
 #endif
 
 static struct platform_driver rbtx4939_flash_driver = {
        .probe          = rbtx4939_flash_probe,
        .remove         = rbtx4939_flash_remove,
-       .suspend        = rbtx4939_flash_suspend,
-       .resume         = rbtx4939_flash_resume,
        .shutdown       = rbtx4939_flash_shutdown,
        .driver         = {
                .name   = "rbtx4939-flash",
index 05e9362dc7f0fdd252d7c7ecb5d6922cf000de98..c6210f5118d1f13ab690a16a479888a2019adfa6 100644 (file)
@@ -415,25 +415,6 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM
-static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state)
-{
-       struct sa_info *info = platform_get_drvdata(dev);
-       int ret = 0;
-
-       if (info)
-               ret = info->mtd->suspend(info->mtd);
-
-       return ret;
-}
-
-static int sa1100_mtd_resume(struct platform_device *dev)
-{
-       struct sa_info *info = platform_get_drvdata(dev);
-       if (info)
-               info->mtd->resume(info->mtd);
-       return 0;
-}
-
 static void sa1100_mtd_shutdown(struct platform_device *dev)
 {
        struct sa_info *info = platform_get_drvdata(dev);
@@ -441,16 +422,12 @@ static void sa1100_mtd_shutdown(struct platform_device *dev)
                info->mtd->resume(info->mtd);
 }
 #else
-#define sa1100_mtd_suspend NULL
-#define sa1100_mtd_resume  NULL
 #define sa1100_mtd_shutdown NULL
 #endif
 
 static struct platform_driver sa1100_mtd_driver = {
        .probe          = sa1100_mtd_probe,
        .remove         = __exit_p(sa1100_mtd_remove),
-       .suspend        = sa1100_mtd_suspend,
-       .resume         = sa1100_mtd_resume,
        .shutdown       = sa1100_mtd_shutdown,
        .driver         = {
                .name   = "sa1100-mtd",
index 81756e397711f4a53972000472d0ef284f9903e0..d4314fb8821219c78279599a8113efab68f76b4f 100644 (file)
 
 /****************************************************************************/
 
+extern char _ebss;
+
 struct map_info uclinux_ram_map = {
        .name = "RAM",
+       .phys = (unsigned long)&_ebss,
+       .size = 0,
 };
 
-struct mtd_info *uclinux_ram_mtdinfo;
+static struct mtd_info *uclinux_ram_mtdinfo;
 
 /****************************************************************************/
 
-struct mtd_partition uclinux_romfs[] = {
+static struct mtd_partition uclinux_romfs[] = {
        { .name = "ROMfs" }
 };
 
@@ -38,7 +42,7 @@ struct mtd_partition uclinux_romfs[] = {
 
 /****************************************************************************/
 
-int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
+static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
        size_t *retlen, void **virt, resource_size_t *phys)
 {
        struct map_info *map = mtd->priv;
@@ -55,12 +59,10 @@ static int __init uclinux_mtd_init(void)
 {
        struct mtd_info *mtd;
        struct map_info *mapp;
-       extern char _ebss;
-       unsigned long addr = (unsigned long) &_ebss;
 
        mapp = &uclinux_ram_map;
-       mapp->phys = addr;
-       mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(addr + 8))));
+       if (!mapp->size)
+               mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8))));
        mapp->bankwidth = 4;
 
        printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",
index aaac3b6800b7358dd1597df13c0ea781d3215031..c3f62654b6df4bee93cc69a86ca839031186f910 100644 (file)
@@ -291,7 +291,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
        gd->private_data = new;
        new->blkcore_priv = gd;
        gd->queue = tr->blkcore_priv->rq;
-       gd->driverfs_dev = new->mtd->dev.parent;
+       gd->driverfs_dev = &new->mtd->dev;
 
        if (new->readonly)
                set_disk_ro(gd, 1);
index 763d3f0a1f428104d7a630de7746a09bc9883bb9..5b081cb84351340dd6164fe448703f93c6dfca47 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/sched.h>
 #include <linux/smp_lock.h>
 #include <linux/backing-dev.h>
+#include <linux/compat.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/compatmac.h>
@@ -355,6 +356,100 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
 # define otp_select_filemode(f,m)      -EOPNOTSUPP
 #endif
 
+static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
+       uint64_t start, uint32_t length, void __user *ptr,
+       uint32_t __user *retp)
+{
+       struct mtd_oob_ops ops;
+       uint32_t retlen;
+       int ret = 0;
+
+       if (!(file->f_mode & FMODE_WRITE))
+               return -EPERM;
+
+       if (length > 4096)
+               return -EINVAL;
+
+       if (!mtd->write_oob)
+               ret = -EOPNOTSUPP;
+       else
+               ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT;
+
+       if (ret)
+               return ret;
+
+       ops.ooblen = length;
+       ops.ooboffs = start & (mtd->oobsize - 1);
+       ops.datbuf = NULL;
+       ops.mode = MTD_OOB_PLACE;
+
+       if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
+               return -EINVAL;
+
+       ops.oobbuf = kmalloc(length, GFP_KERNEL);
+       if (!ops.oobbuf)
+               return -ENOMEM;
+
+       if (copy_from_user(ops.oobbuf, ptr, length)) {
+               kfree(ops.oobbuf);
+               return -EFAULT;
+       }
+
+       start &= ~((uint64_t)mtd->oobsize - 1);
+       ret = mtd->write_oob(mtd, start, &ops);
+
+       if (ops.oobretlen > 0xFFFFFFFFU)
+               ret = -EOVERFLOW;
+       retlen = ops.oobretlen;
+       if (copy_to_user(retp, &retlen, sizeof(length)))
+               ret = -EFAULT;
+
+       kfree(ops.oobbuf);
+       return ret;
+}
+
+static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
+       uint32_t length, void __user *ptr, uint32_t __user *retp)
+{
+       struct mtd_oob_ops ops;
+       int ret = 0;
+
+       if (length > 4096)
+               return -EINVAL;
+
+       if (!mtd->read_oob)
+               ret = -EOPNOTSUPP;
+       else
+               ret = access_ok(VERIFY_WRITE, ptr,
+                               length) ? 0 : -EFAULT;
+       if (ret)
+               return ret;
+
+       ops.ooblen = length;
+       ops.ooboffs = start & (mtd->oobsize - 1);
+       ops.datbuf = NULL;
+       ops.mode = MTD_OOB_PLACE;
+
+       if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
+               return -EINVAL;
+
+       ops.oobbuf = kmalloc(length, GFP_KERNEL);
+       if (!ops.oobbuf)
+               return -ENOMEM;
+
+       start &= ~((uint64_t)mtd->oobsize - 1);
+       ret = mtd->read_oob(mtd, start, &ops);
+
+       if (put_user(ops.oobretlen, retp))
+               ret = -EFAULT;
+       else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf,
+                                           ops.oobretlen))
+               ret = -EFAULT;
+
+       kfree(ops.oobbuf);
+       return ret;
+}
+
 static int mtd_ioctl(struct inode *inode, struct file *file,
                     u_int cmd, u_long arg)
 {
@@ -417,6 +512,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                break;
 
        case MEMERASE:
+       case MEMERASE64:
        {
                struct erase_info *erase;
 
@@ -427,20 +523,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                if (!erase)
                        ret = -ENOMEM;
                else {
-                       struct erase_info_user einfo;
-
                        wait_queue_head_t waitq;
                        DECLARE_WAITQUEUE(wait, current);
 
                        init_waitqueue_head(&waitq);
 
-                       if (copy_from_user(&einfo, argp,
-                                   sizeof(struct erase_info_user))) {
-                               kfree(erase);
-                               return -EFAULT;
+                       if (cmd == MEMERASE64) {
+                               struct erase_info_user64 einfo64;
+
+                               if (copy_from_user(&einfo64, argp,
+                                           sizeof(struct erase_info_user64))) {
+                                       kfree(erase);
+                                       return -EFAULT;
+                               }
+                               erase->addr = einfo64.start;
+                               erase->len = einfo64.length;
+                       } else {
+                               struct erase_info_user einfo32;
+
+                               if (copy_from_user(&einfo32, argp,
+                                           sizeof(struct erase_info_user))) {
+                                       kfree(erase);
+                                       return -EFAULT;
+                               }
+                               erase->addr = einfo32.start;
+                               erase->len = einfo32.length;
                        }
-                       erase->addr = einfo.start;
-                       erase->len = einfo.length;
                        erase->mtd = mtd;
                        erase->callback = mtdchar_erase_callback;
                        erase->priv = (unsigned long)&waitq;
@@ -474,100 +582,56 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        case MEMWRITEOOB:
        {
                struct mtd_oob_buf buf;
-               struct mtd_oob_ops ops;
-               struct mtd_oob_buf __user *user_buf = argp;
-               uint32_t retlen;
-
-               if(!(file->f_mode & FMODE_WRITE))
-                       return -EPERM;
-
-               if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
-                       return -EFAULT;
-
-               if (buf.length > 4096)
-                       return -EINVAL;
-
-               if (!mtd->write_oob)
-                       ret = -EOPNOTSUPP;
-               else
-                       ret = access_ok(VERIFY_READ, buf.ptr,
-                                       buf.length) ? 0 : EFAULT;
-
-               if (ret)
-                       return ret;
-
-               ops.ooblen = buf.length;
-               ops.ooboffs = buf.start & (mtd->oobsize - 1);
-               ops.datbuf = NULL;
-               ops.mode = MTD_OOB_PLACE;
-
-               if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
-                       return -EINVAL;
-
-               ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
-               if (!ops.oobbuf)
-                       return -ENOMEM;
-
-               if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
-                       kfree(ops.oobbuf);
-                       return -EFAULT;
-               }
+               struct mtd_oob_buf __user *buf_user = argp;
 
-               buf.start &= ~(mtd->oobsize - 1);
-               ret = mtd->write_oob(mtd, buf.start, &ops);
-
-               if (ops.oobretlen > 0xFFFFFFFFU)
-                       ret = -EOVERFLOW;
-               retlen = ops.oobretlen;
-               if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length)))
+               /* NOTE: writes return length to buf_user->length */
+               if (copy_from_user(&buf, argp, sizeof(buf)))
                        ret = -EFAULT;
-
-               kfree(ops.oobbuf);
+               else
+                       ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
+                               buf.ptr, &buf_user->length);
                break;
-
        }
 
        case MEMREADOOB:
        {
                struct mtd_oob_buf buf;
-               struct mtd_oob_ops ops;
-
-               if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
-                       return -EFAULT;
-
-               if (buf.length > 4096)
-                       return -EINVAL;
+               struct mtd_oob_buf __user *buf_user = argp;
 
-               if (!mtd->read_oob)
-                       ret = -EOPNOTSUPP;
+               /* NOTE: writes return length to buf_user->start */
+               if (copy_from_user(&buf, argp, sizeof(buf)))
+                       ret = -EFAULT;
                else
-                       ret = access_ok(VERIFY_WRITE, buf.ptr,
-                                       buf.length) ? 0 : -EFAULT;
-               if (ret)
-                       return ret;
-
-               ops.ooblen = buf.length;
-               ops.ooboffs = buf.start & (mtd->oobsize - 1);
-               ops.datbuf = NULL;
-               ops.mode = MTD_OOB_PLACE;
+                       ret = mtd_do_readoob(mtd, buf.start, buf.length,
+                               buf.ptr, &buf_user->start);
+               break;
+       }
 
-               if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
-                       return -EINVAL;
+       case MEMWRITEOOB64:
+       {
+               struct mtd_oob_buf64 buf;
+               struct mtd_oob_buf64 __user *buf_user = argp;
 
-               ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
-               if (!ops.oobbuf)
-                       return -ENOMEM;
+               if (copy_from_user(&buf, argp, sizeof(buf)))
+                       ret = -EFAULT;
+               else
+                       ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
+                               (void __user *)(uintptr_t)buf.usr_ptr,
+                               &buf_user->length);
+               break;
+       }
 
-               buf.start &= ~(mtd->oobsize - 1);
-               ret = mtd->read_oob(mtd, buf.start, &ops);
+       case MEMREADOOB64:
+       {
+               struct mtd_oob_buf64 buf;
+               struct mtd_oob_buf64 __user *buf_user = argp;
 
-               if (put_user(ops.oobretlen, (uint32_t __user *)argp))
-                       ret = -EFAULT;
-               else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf,
-                                                   ops.oobretlen))
+               if (copy_from_user(&buf, argp, sizeof(buf)))
                        ret = -EFAULT;
-
-               kfree(ops.oobbuf);
+               else
+                       ret = mtd_do_readoob(mtd, buf.start, buf.length,
+                               (void __user *)(uintptr_t)buf.usr_ptr,
+                               &buf_user->length);
                break;
        }
 
@@ -758,6 +822,68 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        return ret;
 } /* memory_ioctl */
 
+#ifdef CONFIG_COMPAT
+
+struct mtd_oob_buf32 {
+       u_int32_t start;
+       u_int32_t length;
+       compat_caddr_t ptr;     /* unsigned char* */
+};
+
+#define MEMWRITEOOB32          _IOWR('M', 3, struct mtd_oob_buf32)
+#define MEMREADOOB32           _IOWR('M', 4, struct mtd_oob_buf32)
+
+static long mtd_compat_ioctl(struct file *file, unsigned int cmd,
+       unsigned long arg)
+{
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct mtd_file_info *mfi = file->private_data;
+       struct mtd_info *mtd = mfi->mtd;
+       void __user *argp = compat_ptr(arg);
+       int ret = 0;
+
+       lock_kernel();
+
+       switch (cmd) {
+       case MEMWRITEOOB32:
+       {
+               struct mtd_oob_buf32 buf;
+               struct mtd_oob_buf32 __user *buf_user = argp;
+
+               if (copy_from_user(&buf, argp, sizeof(buf)))
+                       ret = -EFAULT;
+               else
+                       ret = mtd_do_writeoob(file, mtd, buf.start,
+                               buf.length, compat_ptr(buf.ptr),
+                               &buf_user->length);
+               break;
+       }
+
+       case MEMREADOOB32:
+       {
+               struct mtd_oob_buf32 buf;
+               struct mtd_oob_buf32 __user *buf_user = argp;
+
+               /* NOTE: writes return length to buf->start */
+               if (copy_from_user(&buf, argp, sizeof(buf)))
+                       ret = -EFAULT;
+               else
+                       ret = mtd_do_readoob(mtd, buf.start,
+                               buf.length, compat_ptr(buf.ptr),
+                               &buf_user->start);
+               break;
+       }
+       default:
+               ret = mtd_ioctl(inode, file, cmd, (unsigned long)argp);
+       }
+
+       unlock_kernel();
+
+       return ret;
+}
+
+#endif /* CONFIG_COMPAT */
+
 /*
  * try to determine where a shared mapping can be made
  * - only supported for NOMMU at the moment (MMU can't doesn't copy private
@@ -817,6 +943,9 @@ static const struct file_operations mtd_fops = {
        .read           = mtd_read,
        .write          = mtd_write,
        .ioctl          = mtd_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = mtd_compat_ioctl,
+#endif
        .open           = mtd_open,
        .release        = mtd_close,
        .mmap           = mtd_mmap,
index bccb4b1ffc46ff21b127f9e80cafef118f05f3ca..fac54a3fa3f189c60aaf58a197be7867aed4ca80 100644 (file)
 
 #include "mtdcore.h"
 
-
-static struct class *mtd_class;
+static int mtd_cls_suspend(struct device *dev, pm_message_t state);
+static int mtd_cls_resume(struct device *dev);
+
+static struct class mtd_class = {
+       .name = "mtd",
+       .owner = THIS_MODULE,
+       .suspend = mtd_cls_suspend,
+       .resume = mtd_cls_resume,
+};
 
 /* These are exported solely for the purpose of mtd_blkdevs.c. You
    should not use them for _anything_ else */
@@ -52,7 +59,26 @@ static void mtd_release(struct device *dev)
 
        /* remove /dev/mtdXro node if needed */
        if (index)
-               device_destroy(mtd_class, index + 1);
+               device_destroy(&mtd_class, index + 1);
+}
+
+static int mtd_cls_suspend(struct device *dev, pm_message_t state)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+       
+       if (mtd->suspend)
+               return mtd->suspend(mtd);
+       else
+               return 0;
+}
+
+static int mtd_cls_resume(struct device *dev)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+       
+       if (mtd->resume)
+               mtd->resume(mtd);
+       return 0;
 }
 
 static ssize_t mtd_type_show(struct device *dev,
@@ -269,7 +295,7 @@ int add_mtd_device(struct mtd_info *mtd)
                         * physical device.
                         */
                        mtd->dev.type = &mtd_devtype;
-                       mtd->dev.class = mtd_class;
+                       mtd->dev.class = &mtd_class;
                        mtd->dev.devt = MTD_DEVT(i);
                        dev_set_name(&mtd->dev, "mtd%d", i);
                        if (device_register(&mtd->dev) != 0) {
@@ -278,7 +304,7 @@ int add_mtd_device(struct mtd_info *mtd)
                        }
 
                        if (MTD_DEVT(i))
-                               device_create(mtd_class, mtd->dev.parent,
+                               device_create(&mtd_class, mtd->dev.parent,
                                                MTD_DEVT(i) + 1,
                                                NULL, "mtd%dro", i);
 
@@ -604,11 +630,12 @@ done:
 
 static int __init init_mtd(void)
 {
-       mtd_class = class_create(THIS_MODULE, "mtd");
+       int ret;
+       ret = class_register(&mtd_class);
 
-       if (IS_ERR(mtd_class)) {
-               pr_err("Error creating mtd class.\n");
-               return PTR_ERR(mtd_class);
+       if (ret) {
+               pr_err("Error registering mtd class: %d\n", ret);
+               return ret;
        }
 #ifdef CONFIG_PROC_FS
        if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
@@ -623,7 +650,7 @@ static void __exit cleanup_mtd(void)
         if (proc_mtd)
                remove_proc_entry( "mtd", NULL);
 #endif /* CONFIG_PROC_FS */
-       class_destroy(mtd_class);
+       class_unregister(&mtd_class);
 }
 
 module_init(init_mtd);
index 29675edb44b46180bb954ed520f3d329c451a83d..349fcbe5cc0ff2718c7372bf3263b17a7af3c8d3 100644 (file)
@@ -27,9 +27,7 @@ struct mtd_part {
        struct mtd_info mtd;
        struct mtd_info *master;
        uint64_t offset;
-       int index;
        struct list_head list;
-       int registered;
 };
 
 /*
@@ -321,8 +319,7 @@ int del_mtd_partitions(struct mtd_info *master)
        list_for_each_entry_safe(slave, next, &mtd_partitions, list)
                if (slave->master == master) {
                        list_del(&slave->list);
-                       if (slave->registered)
-                               del_mtd_device(&slave->mtd);
+                       del_mtd_device(&slave->mtd);
                        kfree(slave);
                }
 
@@ -395,7 +392,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
                slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
        if (master->sync)
                slave->mtd.sync = part_sync;
-       if (!partno && master->suspend && master->resume) {
+       if (!partno && !master->dev.class && master->suspend && master->resume) {
                        slave->mtd.suspend = part_suspend;
                        slave->mtd.resume = part_resume;
        }
@@ -412,7 +409,6 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
        slave->mtd.erase = part_erase;
        slave->master = master;
        slave->offset = part->offset;
-       slave->index = partno;
 
        if (slave->offset == MTDPART_OFS_APPEND)
                slave->offset = cur_offset;
@@ -500,15 +496,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
        }
 
 out_register:
-       if (part->mtdp) {
-               /* store the object pointer (caller may or may not register it*/
-               *part->mtdp = &slave->mtd;
-               slave->registered = 0;
-       } else {
-               /* register our partition */
-               add_mtd_device(&slave->mtd);
-               slave->registered = 1;
-       }
+       /* register our partition */
+       add_mtd_device(&slave->mtd);
+
        return slave;
 }
 
index f3276897859e2b5e72af0317ab2b7c0a80e810be..ce96c091f01be39b23b7b255b84779bfe58e8f32 100644 (file)
@@ -74,6 +74,12 @@ config MTD_NAND_AMS_DELTA
        help
          Support for NAND flash on Amstrad E3 (Delta).
 
+config MTD_NAND_OMAP2
+       tristate "NAND Flash device on OMAP2 and OMAP3"
+       depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3)
+       help
+          Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms.
+
 config MTD_NAND_TS7250
        tristate "NAND Flash device on TS-7250 board"
        depends on MACH_TS72XX
@@ -139,27 +145,27 @@ config MTD_NAND_PPCHAMELEONEVB
          This enables the NAND flash driver on the PPChameleon EVB Board.
 
 config MTD_NAND_S3C2410
-       tristate "NAND Flash support for S3C2410/S3C2440 SoC"
-       depends on ARCH_S3C2410
+       tristate "NAND Flash support for Samsung S3C SoCs"
+       depends on ARCH_S3C2410 || ARCH_S3C64XX
        help
-         This enables the NAND flash controller on the S3C2410 and S3C2440
+         This enables the NAND flash controller on the S3C24xx and S3C64xx
          SoCs
 
          No board specific support is done by this driver, each board
          must advertise a platform_device for the driver to attach.
 
 config MTD_NAND_S3C2410_DEBUG
-       bool "S3C2410 NAND driver debug"
+       bool "Samsung S3C NAND driver debug"
        depends on MTD_NAND_S3C2410
        help
-         Enable debugging of the S3C2410 NAND driver
+         Enable debugging of the S3C NAND driver
 
 config MTD_NAND_S3C2410_HWECC
-       bool "S3C2410 NAND Hardware ECC"
+       bool "Samsung S3C NAND Hardware ECC"
        depends on MTD_NAND_S3C2410
        help
-         Enable the use of the S3C2410's internal ECC generator when
-         using NAND. Early versions of the chip have had problems with
+         Enable the use of the controller's internal ECC generator when
+         using NAND. Early versions of the chips have had problems with
          incorrect ECC generation, and if using these, the default of
          software ECC is preferable.
 
@@ -171,7 +177,7 @@ config MTD_NAND_NDFC
         NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
 
 config MTD_NAND_S3C2410_CLKSTOP
-       bool "S3C2410 NAND IDLE clock stop"
+       bool "Samsung S3C NAND IDLE clock stop"
        depends on MTD_NAND_S3C2410
        default n
        help
index d33860ac42c396a4f500d6498daf9559787b50c7..f3a786b3cff377b182a15ac41ad88546ca4fa940 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_NAND_CS553X)         += cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)            += ndfc.o
 obj-$(CONFIG_MTD_NAND_ATMEL)           += atmel_nand.o
 obj-$(CONFIG_MTD_NAND_GPIO)            += gpio.o
+obj-$(CONFIG_MTD_NAND_OMAP2)           += omap2.o
 obj-$(CONFIG_MTD_NAND_CM_X270)         += cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)   += excite_nandflash.o
 obj-$(CONFIG_MTD_NAND_PXA3xx)          += pxa3xx_nand.o
index 47a33cec3793d4b3f1a8b512e3fcf95d5ca83aba..2802992b39daf67ce5c917103f03501d113f290e 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -47,6 +48,9 @@
 #define no_ecc         0
 #endif
 
+static int on_flash_bbt = 0;
+module_param(on_flash_bbt, int, 0);
+
 /* Register access macros */
 #define ecc_readl(add, reg)                            \
        __raw_readl(add + ATMEL_ECC_##reg)
@@ -459,12 +463,17 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
 
        if (host->board->det_pin) {
                if (gpio_get_value(host->board->det_pin)) {
-                       printk("No SmartMedia card inserted.\n");
+                       printk(KERN_INFO "No SmartMedia card inserted.\n");
                        res = ENXIO;
                        goto err_no_card;
                }
        }
 
+       if (on_flash_bbt) {
+               printk(KERN_INFO "atmel_nand: Use On Flash BBT\n");
+               nand_chip->options |= NAND_USE_FLASH_BBT;
+       }
+
        /* first scan to find the device and get the page size */
        if (nand_scan_ident(mtd, 1)) {
                res = -ENXIO;
index 4c2a67ca801e3e07c4ebbfa82e449b3210a296d3..8506e7e606fdff97113a16c9beda33a5b0975ebb 100644 (file)
@@ -458,7 +458,7 @@ static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
+static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
                                uint8_t *buf, int is_read)
 {
        struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
@@ -496,11 +496,20 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
        /* setup DMA register with Blackfin DMA API */
        set_dma_config(CH_NFC, 0x0);
        set_dma_start_addr(CH_NFC, (unsigned long) buf);
+
+/* The DMAs have different size on BF52x and BF54x */
+#ifdef CONFIG_BF52x
+       set_dma_x_count(CH_NFC, (page_size >> 1));
+       set_dma_x_modify(CH_NFC, 2);
+       val = DI_EN | WDSIZE_16;
+#endif
+
+#ifdef CONFIG_BF54x
        set_dma_x_count(CH_NFC, (page_size >> 2));
        set_dma_x_modify(CH_NFC, 4);
-
-       /* setup write or read operation */
        val = DI_EN | WDSIZE_32;
+#endif
+       /* setup write or read operation */
        if (is_read)
                val |= WNR;
        set_dma_config(CH_NFC, val);
@@ -512,8 +521,6 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
        else
                bfin_write_NFC_PGCTL(0x2);
        wait_for_completion(&info->dma_completion);
-
-       return 0;
 }
 
 static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
index 02700f769b8aaca397e830068aeb93419d498834..0fad6487e6f4bcb062334ae082126d73ad41cd59 100644 (file)
@@ -44,7 +44,7 @@
  * and some flavors of secondary chipselect (e.g. based on A12) as used
  * with multichip packages.
  *
- * The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC
+ * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC
  * available on chips like the DM355 and OMAP-L137 and needed with the
  * more error-prone MLC NAND chips.
  *
 struct davinci_nand_info {
        struct mtd_info         mtd;
        struct nand_chip        chip;
+       struct nand_ecclayout   ecclayout;
 
        struct device           *dev;
        struct clk              *clk;
        bool                    partitioned;
 
+       bool                    is_readmode;
+
        void __iomem            *base;
        void __iomem            *vaddr;
 
@@ -73,6 +76,7 @@ struct davinci_nand_info {
 };
 
 static DEFINE_SPINLOCK(davinci_nand_lock);
+static bool ecc4_busy;
 
 #define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
 
@@ -217,6 +221,192 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
 
 /*----------------------------------------------------------------------*/
 
+/*
+ * 4-bit hardware ECC ... context maintained over entire AEMIF
+ *
+ * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME
+ * since that forces use of a problematic "infix OOB" layout.
+ * Among other things, it trashes manufacturer bad block markers.
+ * Also, and specific to this hardware, it ECC-protects the "prepad"
+ * in the OOB ... while having ECC protection for parts of OOB would
+ * seem useful, the current MTD stack sometimes wants to update the
+ * OOB without recomputing ECC.
+ */
+
+static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
+{
+       struct davinci_nand_info *info = to_davinci_nand(mtd);
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&davinci_nand_lock, flags);
+
+       /* Start 4-bit ECC calculation for read/write */
+       val = davinci_nand_readl(info, NANDFCR_OFFSET);
+       val &= ~(0x03 << 4);
+       val |= (info->core_chipsel << 4) | BIT(12);
+       davinci_nand_writel(info, NANDFCR_OFFSET, val);
+
+       info->is_readmode = (mode == NAND_ECC_READ);
+
+       spin_unlock_irqrestore(&davinci_nand_lock, flags);
+}
+
+/* Read raw ECC code after writing to NAND. */
+static void
+nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4])
+{
+       const u32 mask = 0x03ff03ff;
+
+       code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask;
+       code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask;
+       code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask;
+       code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask;
+}
+
+/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */
+static int nand_davinci_calculate_4bit(struct mtd_info *mtd,
+               const u_char *dat, u_char *ecc_code)
+{
+       struct davinci_nand_info *info = to_davinci_nand(mtd);
+       u32 raw_ecc[4], *p;
+       unsigned i;
+
+       /* After a read, terminate ECC calculation by a dummy read
+        * of some 4-bit ECC register.  ECC covers everything that
+        * was read; correct() just uses the hardware state, so
+        * ecc_code is not needed.
+        */
+       if (info->is_readmode) {
+               davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
+               return 0;
+       }
+
+       /* Pack eight raw 10-bit ecc values into ten bytes, making
+        * two passes which each convert four values (in upper and
+        * lower halves of two 32-bit words) into five bytes.  The
+        * ROM boot loader uses this same packing scheme.
+        */
+       nand_davinci_readecc_4bit(info, raw_ecc);
+       for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
+               *ecc_code++ =   p[0]        & 0xff;
+               *ecc_code++ = ((p[0] >>  8) & 0x03) | ((p[0] >> 14) & 0xfc);
+               *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] <<  4) & 0xf0);
+               *ecc_code++ = ((p[1] >>  4) & 0x3f) | ((p[1] >> 10) & 0xc0);
+               *ecc_code++ =  (p[1] >> 18) & 0xff;
+       }
+
+       return 0;
+}
+
+/* Correct up to 4 bits in data we just read, using state left in the
+ * hardware plus the ecc_code computed when it was first written.
+ */
+static int nand_davinci_correct_4bit(struct mtd_info *mtd,
+               u_char *data, u_char *ecc_code, u_char *null)
+{
+       int i;
+       struct davinci_nand_info *info = to_davinci_nand(mtd);
+       unsigned short ecc10[8];
+       unsigned short *ecc16;
+       u32 syndrome[4];
+       unsigned num_errors, corrected;
+
+       /* All bytes 0xff?  It's an erased page; ignore its ECC. */
+       for (i = 0; i < 10; i++) {
+               if (ecc_code[i] != 0xff)
+                       goto compare;
+       }
+       return 0;
+
+compare:
+       /* Unpack ten bytes into eight 10 bit values.  We know we're
+        * little-endian, and use type punning for less shifting/masking.
+        */
+       if (WARN_ON(0x01 & (unsigned) ecc_code))
+               return -EINVAL;
+       ecc16 = (unsigned short *)ecc_code;
+
+       ecc10[0] =  (ecc16[0] >>  0) & 0x3ff;
+       ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0);
+       ecc10[2] =  (ecc16[1] >>  4) & 0x3ff;
+       ecc10[3] = ((ecc16[1] >> 14) & 0x3)  | ((ecc16[2] << 2) & 0x3fc);
+       ecc10[4] =  (ecc16[2] >>  8)         | ((ecc16[3] << 8) & 0x300);
+       ecc10[5] =  (ecc16[3] >>  2) & 0x3ff;
+       ecc10[6] = ((ecc16[3] >> 12) & 0xf)  | ((ecc16[4] << 4) & 0x3f0);
+       ecc10[7] =  (ecc16[4] >>  6) & 0x3ff;
+
+       /* Tell ECC controller about the expected ECC codes. */
+       for (i = 7; i >= 0; i--)
+               davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]);
+
+       /* Allow time for syndrome calculation ... then read it.
+        * A syndrome of all zeroes 0 means no detected errors.
+        */
+       davinci_nand_readl(info, NANDFSR_OFFSET);
+       nand_davinci_readecc_4bit(info, syndrome);
+       if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3]))
+               return 0;
+
+       /* Start address calculation, and wait for it to complete.
+        * We _could_ start reading more data while this is working,
+        * to speed up the overall page read.
+        */
+       davinci_nand_writel(info, NANDFCR_OFFSET,
+                       davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13));
+       for (;;) {
+               u32     fsr = davinci_nand_readl(info, NANDFSR_OFFSET);
+
+               switch ((fsr >> 8) & 0x0f) {
+               case 0:         /* no error, should not happen */
+                       return 0;
+               case 1:         /* five or more errors detected */
+                       return -EIO;
+               case 2:         /* error addresses computed */
+               case 3:
+                       num_errors = 1 + ((fsr >> 16) & 0x03);
+                       goto correct;
+               default:        /* still working on it */
+                       cpu_relax();
+                       continue;
+               }
+       }
+
+correct:
+       /* correct each error */
+       for (i = 0, corrected = 0; i < num_errors; i++) {
+               int error_address, error_value;
+
+               if (i > 1) {
+                       error_address = davinci_nand_readl(info,
+                                               NAND_ERR_ADD2_OFFSET);
+                       error_value = davinci_nand_readl(info,
+                                               NAND_ERR_ERRVAL2_OFFSET);
+               } else {
+                       error_address = davinci_nand_readl(info,
+                                               NAND_ERR_ADD1_OFFSET);
+                       error_value = davinci_nand_readl(info,
+                                               NAND_ERR_ERRVAL1_OFFSET);
+               }
+
+               if (i & 1) {
+                       error_address >>= 16;
+                       error_value >>= 16;
+               }
+               error_address &= 0x3ff;
+               error_address = (512 + 7) - error_address;
+
+               if (error_address < 512) {
+                       data[error_address] ^= error_value;
+                       corrected++;
+               }
+       }
+
+       return corrected;
+}
+
+/*----------------------------------------------------------------------*/
+
 /*
  * NOTE:  NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
  * how these chips are normally wired.  This translates to both 8 and 16
@@ -294,6 +484,23 @@ static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info)
 
 /*----------------------------------------------------------------------*/
 
+/* An ECC layout for using 4-bit ECC with small-page flash, storing
+ * ten ECC bytes plus the manufacturer's bad block marker byte, and
+ * and not overlapping the default BBT markers.
+ */
+static struct nand_ecclayout hwecc4_small __initconst = {
+       .eccbytes = 10,
+       .eccpos = { 0, 1, 2, 3, 4,
+               /* offset 5 holds the badblock marker */
+               6, 7,
+               13, 14, 15, },
+       .oobfree = {
+               {.offset = 8, .length = 5, },
+               {.offset = 16, },
+       },
+};
+
+
 static int __init nand_davinci_probe(struct platform_device *pdev)
 {
        struct davinci_nand_pdata       *pdata = pdev->dev.platform_data;
@@ -306,6 +513,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
        uint32_t                        val;
        nand_ecc_modes_t                ecc_mode;
 
+       /* insist on board-specific configuration */
+       if (!pdata)
+               return -ENODEV;
+
        /* which external chipselect will we be managing? */
        if (pdev->id < 0 || pdev->id > 3)
                return -ENODEV;
@@ -351,7 +562,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
        info->chip.select_chip  = nand_davinci_select_chip;
 
        /* options such as NAND_USE_FLASH_BBT or 16-bit widths */
-       info->chip.options      = pdata ? pdata->options : 0;
+       info->chip.options      = pdata->options;
 
        info->ioaddr            = (uint32_t __force) vaddr;
 
@@ -360,14 +571,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
        info->mask_chipsel      = pdata->mask_chipsel;
 
        /* use nandboot-capable ALE/CLE masks by default */
-       if (pdata && pdata->mask_ale)
-               info->mask_ale  = pdata->mask_cle;
-       else
-               info->mask_ale  = MASK_ALE;
-       if (pdata && pdata->mask_cle)
-               info->mask_cle  = pdata->mask_cle;
-       else
-               info->mask_cle  = MASK_CLE;
+       info->mask_ale          = pdata->mask_cle ? : MASK_ALE;
+       info->mask_cle          = pdata->mask_cle ? : MASK_CLE;
 
        /* Set address of hardware control function */
        info->chip.cmd_ctrl     = nand_davinci_hwcontrol;
@@ -377,30 +582,44 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
        info->chip.read_buf     = nand_davinci_read_buf;
        info->chip.write_buf    = nand_davinci_write_buf;
 
-       /* use board-specific ECC config; else, the best available */
-       if (pdata)
-               ecc_mode = pdata->ecc_mode;
-       else
-               ecc_mode = NAND_ECC_HW;
+       /* Use board-specific ECC config */
+       ecc_mode                = pdata->ecc_mode;
 
+       ret = -EINVAL;
        switch (ecc_mode) {
        case NAND_ECC_NONE:
        case NAND_ECC_SOFT:
+               pdata->ecc_bits = 0;
                break;
        case NAND_ECC_HW:
-               info->chip.ecc.calculate = nand_davinci_calculate_1bit;
-               info->chip.ecc.correct = nand_davinci_correct_1bit;
-               info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
+               if (pdata->ecc_bits == 4) {
+                       /* No sanity checks:  CPUs must support this,
+                        * and the chips may not use NAND_BUSWIDTH_16.
+                        */
+
+                       /* No sharing 4-bit hardware between chipselects yet */
+                       spin_lock_irq(&davinci_nand_lock);
+                       if (ecc4_busy)
+                               ret = -EBUSY;
+                       else
+                               ecc4_busy = true;
+                       spin_unlock_irq(&davinci_nand_lock);
+
+                       if (ret == -EBUSY)
+                               goto err_ecc;
+
+                       info->chip.ecc.calculate = nand_davinci_calculate_4bit;
+                       info->chip.ecc.correct = nand_davinci_correct_4bit;
+                       info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
+                       info->chip.ecc.bytes = 10;
+               } else {
+                       info->chip.ecc.calculate = nand_davinci_calculate_1bit;
+                       info->chip.ecc.correct = nand_davinci_correct_1bit;
+                       info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
+                       info->chip.ecc.bytes = 3;
+               }
                info->chip.ecc.size = 512;
-               info->chip.ecc.bytes = 3;
                break;
-       case NAND_ECC_HW_SYNDROME:
-               /* FIXME implement */
-               info->chip.ecc.size = 512;
-               info->chip.ecc.bytes = 10;
-
-               dev_warn(&pdev->dev, "4-bit ECC nyet supported\n");
-               /* FALL THROUGH */
        default:
                ret = -EINVAL;
                goto err_ecc;
@@ -441,12 +660,56 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
        spin_unlock_irq(&davinci_nand_lock);
 
        /* Scan to find existence of the device(s) */
-       ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1);
+       ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1);
        if (ret < 0) {
                dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
                goto err_scan;
        }
 
+       /* Update ECC layout if needed ... for 1-bit HW ECC, the default
+        * is OK, but it allocates 6 bytes when only 3 are needed (for
+        * each 512 bytes).  For the 4-bit HW ECC, that default is not
+        * usable:  10 bytes are needed, not 6.
+        */
+       if (pdata->ecc_bits == 4) {
+               int     chunks = info->mtd.writesize / 512;
+
+               if (!chunks || info->mtd.oobsize < 16) {
+                       dev_dbg(&pdev->dev, "too small\n");
+                       ret = -EINVAL;
+                       goto err_scan;
+               }
+
+               /* For small page chips, preserve the manufacturer's
+                * badblock marking data ... and make sure a flash BBT
+                * table marker fits in the free bytes.
+                */
+               if (chunks == 1) {
+                       info->ecclayout = hwecc4_small;
+                       info->ecclayout.oobfree[1].length =
+                               info->mtd.oobsize - 16;
+                       goto syndrome_done;
+               }
+
+               /* For large page chips we'll be wanting to use a
+                * not-yet-implemented mode that reads OOB data
+                * before reading the body of the page, to avoid
+                * the "infix OOB" model of NAND_ECC_HW_SYNDROME
+                * (and preserve manufacturer badblock markings).
+                */
+               dev_warn(&pdev->dev, "no 4-bit ECC support yet "
+                               "for large page NAND\n");
+               ret = -EIO;
+               goto err_scan;
+
+syndrome_done:
+               info->chip.ecc.layout = &info->ecclayout;
+       }
+
+       ret = nand_scan_tail(&info->mtd);
+       if (ret < 0)
+               goto err_scan;
+
        if (mtd_has_partitions()) {
                struct mtd_partition    *mtd_parts = NULL;
                int                     mtd_parts_nb = 0;
@@ -455,22 +718,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
                        static const char *probes[] __initconst =
                                { "cmdlinepart", NULL };
 
-                       const char              *master_name;
-
-                       /* Set info->mtd.name = 0 temporarily */
-                       master_name             = info->mtd.name;
-                       info->mtd.name          = (char *)0;
-
-                       /* info->mtd.name == 0, means: don't bother checking
-                          <mtd-id> */
                        mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
                                                            &mtd_parts, 0);
-
-                       /* Restore info->mtd.name */
-                       info->mtd.name = master_name;
                }
 
-               if (mtd_parts_nb <= 0 && pdata) {
+               if (mtd_parts_nb <= 0) {
                        mtd_parts = pdata->parts;
                        mtd_parts_nb = pdata->nr_parts;
                }
@@ -483,7 +735,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
                                info->partitioned = true;
                }
 
-       } else if (pdata && pdata->nr_parts) {
+       } else if (pdata->nr_parts) {
                dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n",
                                pdata->nr_parts, info->mtd.name);
        }
@@ -509,6 +761,11 @@ err_scan:
 err_clk_enable:
        clk_put(info->clk);
 
+       spin_lock_irq(&davinci_nand_lock);
+       if (ecc_mode == NAND_ECC_HW_SYNDROME)
+               ecc4_busy = false;
+       spin_unlock_irq(&davinci_nand_lock);
+
 err_ecc:
 err_clk:
 err_ioremap:
@@ -532,6 +789,11 @@ static int __exit nand_davinci_remove(struct platform_device *pdev)
        else
                status = del_mtd_device(&info->mtd);
 
+       spin_lock_irq(&davinci_nand_lock);
+       if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
+               ecc4_busy = false;
+       spin_unlock_irq(&davinci_nand_lock);
+
        iounmap(info->base);
        iounmap(info->vaddr);
 
index 40c26080ecdaab563e8cacfd63e716d7f34f172f..76beea40d2cf2d35d6f0f5b32f3b45f404a5704b 100644 (file)
@@ -138,7 +138,14 @@ static struct nand_ecclayout nand_hw_eccoob_8 = {
 static struct nand_ecclayout nand_hw_eccoob_16 = {
        .eccbytes = 5,
        .eccpos = {6, 7, 8, 9, 10},
-       .oobfree = {{0, 6}, {12, 4}, }
+       .oobfree = {{0, 5}, {11, 5}, }
+};
+
+static struct nand_ecclayout nand_hw_eccoob_64 = {
+       .eccbytes = 20,
+       .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
+                  38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
+       .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, }
 };
 
 #ifdef CONFIG_MTD_PARTITIONS
@@ -192,7 +199,7 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries,
                        }
                        udelay(1);
                }
-               if (max_retries <= 0)
+               if (max_retries < 0)
                        DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
                              __func__, param);
        }
@@ -795,9 +802,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
                send_addr(host, (page_addr & 0xff), false);
 
                if (host->pagesize_2k) {
-                       send_addr(host, (page_addr >> 8) & 0xFF, false);
-                       if (mtd->size >= 0x40000000)
+                       if (mtd->size >= 0x10000000) {
+                               /* paddr_8 - paddr_15 */
+                               send_addr(host, (page_addr >> 8) & 0xff, false);
                                send_addr(host, (page_addr >> 16) & 0xff, true);
+                       } else
+                               /* paddr_8 - paddr_15 */
+                               send_addr(host, (page_addr >> 8) & 0xff, true);
                } else {
                        /* One more address cycle for higher density devices */
                        if (mtd->size >= 0x4000000) {
@@ -923,7 +934,6 @@ static int __init mxcnd_probe(struct platform_device *pdev)
                this->ecc.mode = NAND_ECC_HW;
                this->ecc.size = 512;
                this->ecc.bytes = 3;
-               this->ecc.layout = &nand_hw_eccoob_8;
                tmp = readw(host->regs + NFC_CONFIG1);
                tmp |= NFC_ECC_EN;
                writew(tmp, host->regs + NFC_CONFIG1);
@@ -957,12 +967,44 @@ static int __init mxcnd_probe(struct platform_device *pdev)
                this->ecc.layout = &nand_hw_eccoob_16;
        }
 
-       host->pagesize_2k = 0;
+       /* first scan to find the device and get the page size */
+       if (nand_scan_ident(mtd, 1)) {
+               err = -ENXIO;
+               goto escan;
+       }
 
-       /* Scan to find existence of the device */
-       if (nand_scan(mtd, 1)) {
-               DEBUG(MTD_DEBUG_LEVEL0,
-                     "MXC_ND: Unable to find any NAND device.\n");
+       host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0;
+
+       if (this->ecc.mode == NAND_ECC_HW) {
+               switch (mtd->oobsize) {
+               case 8:
+                       this->ecc.layout = &nand_hw_eccoob_8;
+                       break;
+               case 16:
+                       this->ecc.layout = &nand_hw_eccoob_16;
+                       break;
+               case 64:
+                       this->ecc.layout = &nand_hw_eccoob_64;
+                       break;
+               default:
+                       /* page size not handled by HW ECC */
+                       /* switching back to soft ECC */
+                       this->ecc.size = 512;
+                       this->ecc.bytes = 3;
+                       this->ecc.layout = &nand_hw_eccoob_8;
+                       this->ecc.mode = NAND_ECC_SOFT;
+                       this->ecc.calculate = NULL;
+                       this->ecc.correct = NULL;
+                       this->ecc.hwctl = NULL;
+                       tmp = readw(host->regs + NFC_CONFIG1);
+                       tmp &= ~NFC_ECC_EN;
+                       writew(tmp, host->regs + NFC_CONFIG1);
+                       break;
+               }
+       }
+
+       /* second phase scan */
+       if (nand_scan_tail(mtd)) {
                err = -ENXIO;
                goto escan;
        }
@@ -985,7 +1027,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
        return 0;
 
 escan:
-       free_irq(host->irq, NULL);
+       free_irq(host->irq, host);
 eirq:
        iounmap(host->regs);
 eres:
@@ -1005,7 +1047,7 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
        platform_set_drvdata(pdev, NULL);
 
        nand_release(&host->mtd);
-       free_irq(host->irq, NULL);
+       free_irq(host->irq, host);
        iounmap(host->regs);
        kfree(host);
 
index 3d7ed432fa4157ec9d655c3354c4c1edc699ebc8..8c21b89d2d0c2a7ea79eccf0d9c840771c68a58a 100644 (file)
@@ -2756,7 +2756,8 @@ int nand_scan_tail(struct mtd_info *mtd)
         * the out of band area
         */
        chip->ecc.layout->oobavail = 0;
-       for (i = 0; chip->ecc.layout->oobfree[i].length; i++)
+       for (i = 0; chip->ecc.layout->oobfree[i].length
+                       && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
                chip->ecc.layout->oobavail +=
                        chip->ecc.layout->oobfree[i].length;
        mtd->oobavail = chip->ecc.layout->oobavail;
index 868147acce2cec19677605e2bdcf67d8b5e9a14c..c0cb87d6d16e27ec697c77e719e6723eae9ce6de 100644 (file)
@@ -428,8 +428,8 @@ EXPORT_SYMBOL(nand_calculate_ecc);
 int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
                      unsigned char *read_ecc, unsigned char *calc_ecc)
 {
-       unsigned char b0, b1, b2;
-       unsigned char byte_addr, bit_addr;
+       unsigned char b0, b1, b2, bit_addr;
+       unsigned int byte_addr;
        /* 256 or 512 bytes/ecc  */
        const uint32_t eccsize_mult =
                        (((struct nand_chip *)mtd->priv)->ecc.size) >> 8;
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
new file mode 100644 (file)
index 0000000..0cd76f8
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ * Copyright Â© 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
+ * Copyright Â© 2004 Micron Technology Inc.
+ * Copyright Â© 2004 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+#include <asm/dma.h>
+
+#include <mach/gpmc.h>
+#include <mach/nand.h>
+
+#define GPMC_IRQ_STATUS                0x18
+#define GPMC_ECC_CONFIG                0x1F4
+#define GPMC_ECC_CONTROL       0x1F8
+#define GPMC_ECC_SIZE_CONFIG   0x1FC
+#define GPMC_ECC1_RESULT       0x200
+
+#define        DRIVER_NAME     "omap2-nand"
+
+/* size (4 KiB) for IO mapping */
+#define        NAND_IO_SIZE    SZ_4K
+
+#define        NAND_WP_OFF     0
+#define NAND_WP_BIT    0x00000010
+#define WR_RD_PIN_MONITORING   0x00600000
+
+#define        GPMC_BUF_FULL   0x00000001
+#define        GPMC_BUF_EMPTY  0x00000000
+
+#define NAND_Ecc_P1e           (1 << 0)
+#define NAND_Ecc_P2e           (1 << 1)
+#define NAND_Ecc_P4e           (1 << 2)
+#define NAND_Ecc_P8e           (1 << 3)
+#define NAND_Ecc_P16e          (1 << 4)
+#define NAND_Ecc_P32e          (1 << 5)
+#define NAND_Ecc_P64e          (1 << 6)
+#define NAND_Ecc_P128e         (1 << 7)
+#define NAND_Ecc_P256e         (1 << 8)
+#define NAND_Ecc_P512e         (1 << 9)
+#define NAND_Ecc_P1024e                (1 << 10)
+#define NAND_Ecc_P2048e                (1 << 11)
+
+#define NAND_Ecc_P1o           (1 << 16)
+#define NAND_Ecc_P2o           (1 << 17)
+#define NAND_Ecc_P4o           (1 << 18)
+#define NAND_Ecc_P8o           (1 << 19)
+#define NAND_Ecc_P16o          (1 << 20)
+#define NAND_Ecc_P32o          (1 << 21)
+#define NAND_Ecc_P64o          (1 << 22)
+#define NAND_Ecc_P128o         (1 << 23)
+#define NAND_Ecc_P256o         (1 << 24)
+#define NAND_Ecc_P512o         (1 << 25)
+#define NAND_Ecc_P1024o                (1 << 26)
+#define NAND_Ecc_P2048o                (1 << 27)
+
+#define TF(value)      (value ? 1 : 0)
+
+#define P2048e(a)      (TF(a & NAND_Ecc_P2048e)        << 0)
+#define P2048o(a)      (TF(a & NAND_Ecc_P2048o)        << 1)
+#define P1e(a)         (TF(a & NAND_Ecc_P1e)           << 2)
+#define P1o(a)         (TF(a & NAND_Ecc_P1o)           << 3)
+#define P2e(a)         (TF(a & NAND_Ecc_P2e)           << 4)
+#define P2o(a)         (TF(a & NAND_Ecc_P2o)           << 5)
+#define P4e(a)         (TF(a & NAND_Ecc_P4e)           << 6)
+#define P4o(a)         (TF(a & NAND_Ecc_P4o)           << 7)
+
+#define P8e(a)         (TF(a & NAND_Ecc_P8e)           << 0)
+#define P8o(a)         (TF(a & NAND_Ecc_P8o)           << 1)
+#define P16e(a)                (TF(a & NAND_Ecc_P16e)          << 2)
+#define P16o(a)                (TF(a & NAND_Ecc_P16o)          << 3)
+#define P32e(a)                (TF(a & NAND_Ecc_P32e)          << 4)
+#define P32o(a)                (TF(a & NAND_Ecc_P32o)          << 5)
+#define P64e(a)                (TF(a & NAND_Ecc_P64e)          << 6)
+#define P64o(a)                (TF(a & NAND_Ecc_P64o)          << 7)
+
+#define P128e(a)       (TF(a & NAND_Ecc_P128e)         << 0)
+#define P128o(a)       (TF(a & NAND_Ecc_P128o)         << 1)
+#define P256e(a)       (TF(a & NAND_Ecc_P256e)         << 2)
+#define P256o(a)       (TF(a & NAND_Ecc_P256o)         << 3)
+#define P512e(a)       (TF(a & NAND_Ecc_P512e)         << 4)
+#define P512o(a)       (TF(a & NAND_Ecc_P512o)         << 5)
+#define P1024e(a)      (TF(a & NAND_Ecc_P1024e)        << 6)
+#define P1024o(a)      (TF(a & NAND_Ecc_P1024o)        << 7)
+
+#define P8e_s(a)       (TF(a & NAND_Ecc_P8e)           << 0)
+#define P8o_s(a)       (TF(a & NAND_Ecc_P8o)           << 1)
+#define P16e_s(a)      (TF(a & NAND_Ecc_P16e)          << 2)
+#define P16o_s(a)      (TF(a & NAND_Ecc_P16o)          << 3)
+#define P1e_s(a)       (TF(a & NAND_Ecc_P1e)           << 4)
+#define P1o_s(a)       (TF(a & NAND_Ecc_P1o)           << 5)
+#define P2e_s(a)       (TF(a & NAND_Ecc_P2e)           << 6)
+#define P2o_s(a)       (TF(a & NAND_Ecc_P2o)           << 7)
+
+#define P4e_s(a)       (TF(a & NAND_Ecc_P4e)           << 0)
+#define P4o_s(a)       (TF(a & NAND_Ecc_P4o)           << 1)
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+struct omap_nand_info {
+       struct nand_hw_control          controller;
+       struct omap_nand_platform_data  *pdata;
+       struct mtd_info                 mtd;
+       struct mtd_partition            *parts;
+       struct nand_chip                nand;
+       struct platform_device          *pdev;
+
+       int                             gpmc_cs;
+       unsigned long                   phys_base;
+       void __iomem                    *gpmc_cs_baseaddr;
+       void __iomem                    *gpmc_baseaddr;
+};
+
+/**
+ * omap_nand_wp - This function enable or disable the Write Protect feature
+ * @mtd: MTD device structure
+ * @mode: WP ON/OFF
+ */
+static void omap_nand_wp(struct mtd_info *mtd, int mode)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                               struct omap_nand_info, mtd);
+
+       unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG);
+
+       if (mode)
+               config &= ~(NAND_WP_BIT);       /* WP is ON */
+       else
+               config |= (NAND_WP_BIT);        /* WP is OFF */
+
+       __raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG));
+}
+
+/**
+ * omap_hwcontrol - hardware specific access to control-lines
+ * @mtd: MTD device structure
+ * @cmd: command to device
+ * @ctrl:
+ * NAND_NCE: bit 0 -> don't care
+ * NAND_CLE: bit 1 -> Command Latch
+ * NAND_ALE: bit 2 -> Address Latch
+ *
+ * NOTE: boards may use different bits for these!!
+ */
+static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                       struct omap_nand_info, mtd);
+       switch (ctrl) {
+       case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
+               info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_COMMAND;
+               info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_DATA;
+               break;
+
+       case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
+               info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_ADDRESS;
+               info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_DATA;
+               break;
+
+       case NAND_CTRL_CHANGE | NAND_NCE:
+               info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_DATA;
+               info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_DATA;
+               break;
+       }
+
+       if (cmd != NAND_CMD_NONE)
+               __raw_writeb(cmd, info->nand.IO_ADDR_W);
+}
+
+/**
+ * omap_read_buf16 - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+       struct nand_chip *nand = mtd->priv;
+
+       __raw_readsw(nand->IO_ADDR_R, buf, len / 2);
+}
+
+/**
+ * omap_write_buf16 - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                               struct omap_nand_info, mtd);
+       u16 *p = (u16 *) buf;
+
+       /* FIXME try bursts of writesw() or DMA ... */
+       len >>= 1;
+
+       while (len--) {
+               writew(*p++, info->nand.IO_ADDR_W);
+
+               while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
+                                               GPMC_STATUS) & GPMC_BUF_FULL))
+                       ;
+       }
+}
+/**
+ * omap_verify_buf - Verify chip data against buffer
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
+ */
+static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       u16 *p = (u16 *) buf;
+
+       len >>= 1;
+       while (len--) {
+               if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_MTD_NAND_OMAP_HWECC
+/**
+ * omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller
+ * @mtd: MTD device structure
+ */
+static void omap_hwecc_init(struct mtd_info *mtd)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       struct nand_chip *chip = mtd->priv;
+       unsigned long val = 0x0;
+
+       /* Read from ECC Control Register */
+       val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+       /* Clear all ECC | Enable Reg1 */
+       val = ((0x00000001<<8) | 0x00000001);
+       __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+
+       /* Read from ECC Size Config Register */
+       val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
+       /* ECCSIZE1=512 | Select eccResultsize[0-3] */
+       val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F));
+       __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
+}
+
+/**
+ * gen_true_ecc - This function will generate true ECC value
+ * @ecc_buf: buffer to store ecc code
+ *
+ * This generated true ECC value can be used when correcting
+ * data read from NAND flash memory core
+ */
+static void gen_true_ecc(u8 *ecc_buf)
+{
+       u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
+               ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
+
+       ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
+                       P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
+       ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
+                       P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
+       ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
+                       P1e(tmp) | P2048o(tmp) | P2048e(tmp));
+}
+
+/**
+ * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
+ * @ecc_data1:  ecc code from nand spare area
+ * @ecc_data2:  ecc code from hardware register obtained from hardware ecc
+ * @page_data:  page data
+ *
+ * This function compares two ECC's and indicates if there is an error.
+ * If the error can be corrected it will be corrected to the buffer.
+ */
+static int omap_compare_ecc(u8 *ecc_data1,     /* read from NAND memory */
+                           u8 *ecc_data2,      /* read from register */
+                           u8 *page_data)
+{
+       uint    i;
+       u8      tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
+       u8      comp0_bit[8], comp1_bit[8], comp2_bit[8];
+       u8      ecc_bit[24];
+       u8      ecc_sum = 0;
+       u8      find_bit = 0;
+       uint    find_byte = 0;
+       int     isEccFF;
+
+       isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
+
+       gen_true_ecc(ecc_data1);
+       gen_true_ecc(ecc_data2);
+
+       for (i = 0; i <= 2; i++) {
+               *(ecc_data1 + i) = ~(*(ecc_data1 + i));
+               *(ecc_data2 + i) = ~(*(ecc_data2 + i));
+       }
+
+       for (i = 0; i < 8; i++) {
+               tmp0_bit[i]     = *ecc_data1 % 2;
+               *ecc_data1      = *ecc_data1 / 2;
+       }
+
+       for (i = 0; i < 8; i++) {
+               tmp1_bit[i]      = *(ecc_data1 + 1) % 2;
+               *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
+       }
+
+       for (i = 0; i < 8; i++) {
+               tmp2_bit[i]      = *(ecc_data1 + 2) % 2;
+               *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
+       }
+
+       for (i = 0; i < 8; i++) {
+               comp0_bit[i]     = *ecc_data2 % 2;
+               *ecc_data2       = *ecc_data2 / 2;
+       }
+
+       for (i = 0; i < 8; i++) {
+               comp1_bit[i]     = *(ecc_data2 + 1) % 2;
+               *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
+       }
+
+       for (i = 0; i < 8; i++) {
+               comp2_bit[i]     = *(ecc_data2 + 2) % 2;
+               *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
+       }
+
+       for (i = 0; i < 6; i++)
+               ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
+
+       for (i = 0; i < 8; i++)
+               ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
+
+       for (i = 0; i < 8; i++)
+               ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
+
+       ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
+       ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
+
+       for (i = 0; i < 24; i++)
+               ecc_sum += ecc_bit[i];
+
+       switch (ecc_sum) {
+       case 0:
+               /* Not reached because this function is not called if
+                *  ECC values are equal
+                */
+               return 0;
+
+       case 1:
+               /* Uncorrectable error */
+               DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
+               return -1;
+
+       case 11:
+               /* UN-Correctable error */
+               DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n");
+               return -1;
+
+       case 12:
+               /* Correctable error */
+               find_byte = (ecc_bit[23] << 8) +
+                           (ecc_bit[21] << 7) +
+                           (ecc_bit[19] << 6) +
+                           (ecc_bit[17] << 5) +
+                           (ecc_bit[15] << 4) +
+                           (ecc_bit[13] << 3) +
+                           (ecc_bit[11] << 2) +
+                           (ecc_bit[9]  << 1) +
+                           ecc_bit[7];
+
+               find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
+
+               DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at "
+                               "offset: %d, bit: %d\n", find_byte, find_bit);
+
+               page_data[find_byte] ^= (1 << find_bit);
+
+               return 0;
+       default:
+               if (isEccFF) {
+                       if (ecc_data2[0] == 0 &&
+                           ecc_data2[1] == 0 &&
+                           ecc_data2[2] == 0)
+                               return 0;
+               }
+               DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
+               return -1;
+       }
+}
+
+/**
+ * omap_correct_data - Compares the ECC read with HW generated ECC
+ * @mtd: MTD device structure
+ * @dat: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from HW ECC registers
+ *
+ * Compares the ecc read from nand spare area with ECC registers values
+ * and if ECC's mismached, it will call 'omap_compare_ecc' for error detection
+ * and correction.
+ */
+static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
+                               u_char *read_ecc, u_char *calc_ecc)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       int blockCnt = 0, i = 0, ret = 0;
+
+       /* Ex NAND_ECC_HW12_2048 */
+       if ((info->nand.ecc.mode == NAND_ECC_HW) &&
+                       (info->nand.ecc.size  == 2048))
+               blockCnt = 4;
+       else
+               blockCnt = 1;
+
+       for (i = 0; i < blockCnt; i++) {
+               if (memcmp(read_ecc, calc_ecc, 3) != 0) {
+                       ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
+                       if (ret < 0)
+                               return ret;
+               }
+               read_ecc += 3;
+               calc_ecc += 3;
+               dat      += 512;
+       }
+       return 0;
+}
+
+/**
+ * omap_calcuate_ecc - Generate non-inverted ECC bytes.
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed
+ * @ecc_code: The ecc_code buffer
+ *
+ * Using noninverted ECC can be considered ugly since writing a blank
+ * page ie. padding will clear the ECC bytes. This is no problem as long
+ * nobody is trying to write data on the seemingly unused page. Reading
+ * an erased page will produce an ECC mismatch between generated and read
+ * ECC bytes that has to be dealt with separately.
+ */
+static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+                               u_char *ecc_code)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       unsigned long val = 0x0;
+       unsigned long reg;
+
+       /* Start Reading from HW ECC1_Result = 0x200 */
+       reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT);
+       val = __raw_readl(reg);
+       *ecc_code++ = val;          /* P128e, ..., P1e */
+       *ecc_code++ = val >> 16;    /* P128o, ..., P1o */
+       /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
+       *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
+       reg += 4;
+
+       return 0;
+}
+
+/**
+ * omap_enable_hwecc - This function enables the hardware ecc functionality
+ * @mtd: MTD device structure
+ * @mode: Read/Write mode
+ */
+static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       struct nand_chip *chip = mtd->priv;
+       unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
+       unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG);
+
+       switch (mode) {
+       case NAND_ECC_READ:
+               __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+               /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
+               val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+               break;
+       case NAND_ECC_READSYN:
+                __raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+               /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
+               val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+               break;
+       case NAND_ECC_WRITE:
+               __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+               /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
+               val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+               break;
+       default:
+               DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n",
+                                       mode);
+               break;
+       }
+
+       __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG);
+}
+#endif
+
+/**
+ * omap_wait - wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND Chip structure
+ *
+ * Wait function is called during Program and erase operations and
+ * the way it is called from MTD layer, we should wait till the NAND
+ * chip is ready after the programming/erase operation has completed.
+ *
+ * Erase can take up to 400ms and program up to 20ms according to
+ * general NAND and SmartMedia specs
+ */
+static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+       struct nand_chip *this = mtd->priv;
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       unsigned long timeo = jiffies;
+       int status, state = this->state;
+
+       if (state == FL_ERASING)
+               timeo += (HZ * 400) / 1000;
+       else
+               timeo += (HZ * 20) / 1000;
+
+       this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_COMMAND;
+       this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA;
+
+       __raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W);
+
+       while (time_before(jiffies, timeo)) {
+               status = __raw_readb(this->IO_ADDR_R);
+               if (!(status & 0x40))
+                       break;
+       }
+       return status;
+}
+
+/**
+ * omap_dev_ready - calls the platform specific dev_ready function
+ * @mtd: MTD device structure
+ */
+static int omap_dev_ready(struct mtd_info *mtd)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS);
+
+       if ((val & 0x100) == 0x100) {
+               /* Clear IRQ Interrupt */
+               val |= 0x100;
+               val &= ~(0x0);
+               __raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS);
+       } else {
+               unsigned int cnt = 0;
+               while (cnt++ < 0x1FF) {
+                       if  ((val & 0x100) == 0x100)
+                               return 0;
+                       val = __raw_readl(info->gpmc_baseaddr +
+                                                       GPMC_IRQ_STATUS);
+               }
+       }
+
+       return 1;
+}
+
+static int __devinit omap_nand_probe(struct platform_device *pdev)
+{
+       struct omap_nand_info           *info;
+       struct omap_nand_platform_data  *pdata;
+       int                             err;
+       unsigned long                   val;
+
+
+       pdata = pdev->dev.platform_data;
+       if (pdata == NULL) {
+               dev_err(&pdev->dev, "platform data missing\n");
+               return -ENODEV;
+       }
+
+       info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, info);
+
+       spin_lock_init(&info->controller.lock);
+       init_waitqueue_head(&info->controller.wq);
+
+       info->pdev = pdev;
+
+       info->gpmc_cs           = pdata->cs;
+       info->gpmc_baseaddr     = pdata->gpmc_baseaddr;
+       info->gpmc_cs_baseaddr  = pdata->gpmc_cs_baseaddr;
+
+       info->mtd.priv          = &info->nand;
+       info->mtd.name          = dev_name(&pdev->dev);
+       info->mtd.owner         = THIS_MODULE;
+
+       err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Cannot request GPMC CS\n");
+               goto out_free_info;
+       }
+
+       /* Enable RD PIN Monitoring Reg */
+       if (pdata->dev_ready) {
+               val  = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1);
+               val |= WR_RD_PIN_MONITORING;
+               gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val);
+       }
+
+       val  = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7);
+       val &= ~(0xf << 8);
+       val |=  (0xc & 0xf) << 8;
+       gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val);
+
+       /* NAND write protect off */
+       omap_nand_wp(&info->mtd, NAND_WP_OFF);
+
+       if (!request_mem_region(info->phys_base, NAND_IO_SIZE,
+                               pdev->dev.driver->name)) {
+               err = -EBUSY;
+               goto out_free_cs;
+       }
+
+       info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE);
+       if (!info->nand.IO_ADDR_R) {
+               err = -ENOMEM;
+               goto out_release_mem_region;
+       }
+       info->nand.controller = &info->controller;
+
+       info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
+       info->nand.cmd_ctrl  = omap_hwcontrol;
+
+       /* REVISIT:  only supports 16-bit NAND flash */
+
+       info->nand.read_buf   = omap_read_buf16;
+       info->nand.write_buf  = omap_write_buf16;
+       info->nand.verify_buf = omap_verify_buf;
+
+       /*
+        * If RDY/BSY line is connected to OMAP then use the omap ready
+        * funcrtion and the generic nand_wait function which reads the status
+        * register after monitoring the RDY/BSY line.Otherwise use a standard
+        * chip delay which is slightly more than tR (AC Timing) of the NAND
+        * device and read status register until you get a failure or success
+        */
+       if (pdata->dev_ready) {
+               info->nand.dev_ready = omap_dev_ready;
+               info->nand.chip_delay = 0;
+       } else {
+               info->nand.waitfunc = omap_wait;
+               info->nand.chip_delay = 50;
+       }
+
+       info->nand.options  |= NAND_SKIP_BBTSCAN;
+       if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000)
+                                                               == 0x1000)
+               info->nand.options  |= NAND_BUSWIDTH_16;
+
+#ifdef CONFIG_MTD_NAND_OMAP_HWECC
+       info->nand.ecc.bytes            = 3;
+       info->nand.ecc.size             = 512;
+       info->nand.ecc.calculate        = omap_calculate_ecc;
+       info->nand.ecc.hwctl            = omap_enable_hwecc;
+       info->nand.ecc.correct          = omap_correct_data;
+       info->nand.ecc.mode             = NAND_ECC_HW;
+
+       /* init HW ECC */
+       omap_hwecc_init(&info->mtd);
+#else
+       info->nand.ecc.mode = NAND_ECC_SOFT;
+#endif
+
+       /* DIP switches on some boards change between 8 and 16 bit
+        * bus widths for flash.  Try the other width if the first try fails.
+        */
+       if (nand_scan(&info->mtd, 1)) {
+               info->nand.options ^= NAND_BUSWIDTH_16;
+               if (nand_scan(&info->mtd, 1)) {
+                       err = -ENXIO;
+                       goto out_release_mem_region;
+               }
+       }
+
+#ifdef CONFIG_MTD_PARTITIONS
+       err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
+       if (err > 0)
+               add_mtd_partitions(&info->mtd, info->parts, err);
+       else if (pdata->parts)
+               add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
+       else
+#endif
+               add_mtd_device(&info->mtd);
+
+       platform_set_drvdata(pdev, &info->mtd);
+
+       return 0;
+
+out_release_mem_region:
+       release_mem_region(info->phys_base, NAND_IO_SIZE);
+out_free_cs:
+       gpmc_cs_free(info->gpmc_cs);
+out_free_info:
+       kfree(info);
+
+       return err;
+}
+
+static int omap_nand_remove(struct platform_device *pdev)
+{
+       struct mtd_info *mtd = platform_get_drvdata(pdev);
+       struct omap_nand_info *info = mtd->priv;
+
+       platform_set_drvdata(pdev, NULL);
+       /* Release NAND device, its internal structures and partitions */
+       nand_release(&info->mtd);
+       iounmap(info->nand.IO_ADDR_R);
+       kfree(&info->mtd);
+       return 0;
+}
+
+static struct platform_driver omap_nand_driver = {
+       .probe          = omap_nand_probe,
+       .remove         = omap_nand_remove,
+       .driver         = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omap_nand_init(void)
+{
+       printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
+       return platform_driver_register(&omap_nand_driver);
+}
+
+static void __exit omap_nand_exit(void)
+{
+       platform_driver_unregister(&omap_nand_driver);
+}
+
+module_init(omap_nand_init);
+module_exit(omap_nand_exit);
+
+MODULE_ALIAS(DRIVER_NAME);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
index c2dfd3ea353d61315a8ed4bfc8c056201c30f67e..7ad972229db4317153dee8db1a008333e7c019e9 100644 (file)
@@ -47,6 +47,28 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
        writeb(cmd, nc->IO_ADDR_W + offs);
 }
 
+static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       struct nand_chip *chip = mtd->priv;
+       void __iomem *io_base = chip->IO_ADDR_R;
+       uint64_t *buf64;
+       int i = 0;
+
+       while (len && (unsigned long)buf & 7) {
+               *buf++ = readb(io_base);
+               len--;
+       }
+       buf64 = (uint64_t *)buf;
+       while (i < len/8) {
+               uint64_t x;
+               asm ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base));
+               buf64[i++] = x;
+       }
+       i *= 8;
+       while (i < len)
+               buf[i++] = readb(io_base);
+}
+
 static int __init orion_nand_probe(struct platform_device *pdev)
 {
        struct mtd_info *mtd;
@@ -83,6 +105,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
        nc->priv = board;
        nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
        nc->cmd_ctrl = orion_nand_cmd_ctrl;
+       nc->read_buf = orion_nand_read_buf;
        nc->ecc.mode = NAND_ECC_SOFT;
 
        if (board->chip_delay)
index 86e1d08eee00761f05c1edc710fd178cbc44cef4..4e16c6f5bdd5a7642b4fff683e2e05e2aadf7267 100644 (file)
@@ -61,6 +61,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
        data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl;
        data->chip.dev_ready = pdata->ctrl.dev_ready;
        data->chip.select_chip = pdata->ctrl.select_chip;
+       data->chip.write_buf = pdata->ctrl.write_buf;
+       data->chip.read_buf = pdata->ctrl.read_buf;
        data->chip.chip_delay = pdata->chip.chip_delay;
        data->chip.options |= pdata->chip.options;
 
@@ -70,6 +72,13 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, data);
 
+       /* Handle any platform specific setup */
+       if (pdata->ctrl.probe) {
+               res = pdata->ctrl.probe(pdev);
+               if (res)
+                       goto out;
+       }
+
        /* Scan to find existance of the device */
        if (nand_scan(&data->mtd, 1)) {
                res = -ENXIO;
@@ -86,6 +95,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
                        return 0;
                }
        }
+       if (pdata->chip.set_parts)
+               pdata->chip.set_parts(data->mtd.size, &pdata->chip);
        if (pdata->chip.partitions) {
                data->parts = pdata->chip.partitions;
                res = add_mtd_partitions(&data->mtd, data->parts,
@@ -99,6 +110,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
 
        nand_release(&data->mtd);
 out:
+       if (pdata->ctrl.remove)
+               pdata->ctrl.remove(pdev);
        platform_set_drvdata(pdev, NULL);
        iounmap(data->io_base);
        kfree(data);
@@ -111,15 +124,15 @@ out:
 static int __devexit plat_nand_remove(struct platform_device *pdev)
 {
        struct plat_nand_data *data = platform_get_drvdata(pdev);
-#ifdef CONFIG_MTD_PARTITIONS
        struct platform_nand_data *pdata = pdev->dev.platform_data;
-#endif
 
        nand_release(&data->mtd);
 #ifdef CONFIG_MTD_PARTITIONS
        if (data->parts && data->parts != pdata->chip.partitions)
                kfree(data->parts);
 #endif
+       if (pdata->ctrl.remove)
+               pdata->ctrl.remove(pdev);
        iounmap(data->io_base);
        kfree(data);
 
@@ -128,7 +141,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev)
 
 static struct platform_driver plat_nand_driver = {
        .probe          = plat_nand_probe,
-       .remove         = plat_nand_remove,
+       .remove         = __devexit_p(plat_nand_remove),
        .driver         = {
                .name   = "gen_nand",
                .owner  = THIS_MODULE,
index 8e375d5fe231622953a29bc9ac6431bffcd06121..11dc7e69c4fb51d1a0cd801efa7982aecde91b49 100644 (file)
@@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = {
 
 struct s3c2410_nand_info;
 
+/**
+ * struct s3c2410_nand_mtd - driver MTD structure
+ * @mtd: The MTD instance to pass to the MTD layer.
+ * @chip: The NAND chip information.
+ * @set: The platform information supplied for this set of NAND chips.
+ * @info: Link back to the hardware information.
+ * @scan_res: The result from calling nand_scan_ident().
+*/
 struct s3c2410_nand_mtd {
        struct mtd_info                 mtd;
        struct nand_chip                chip;
@@ -90,6 +98,21 @@ enum s3c_cpu_type {
 
 /* overview of the s3c2410 nand state */
 
+/**
+ * struct s3c2410_nand_info - NAND controller state.
+ * @mtds: An array of MTD instances on this controoler.
+ * @platform: The platform data for this board.
+ * @device: The platform device we bound to.
+ * @area: The IO area resource that came from request_mem_region().
+ * @clk: The clock resource for this controller.
+ * @regs: The area mapped for the hardware registers described by @area.
+ * @sel_reg: Pointer to the register controlling the NAND selection.
+ * @sel_bit: The bit in @sel_reg to select the NAND chip.
+ * @mtd_count: The number of MTDs created from this controller.
+ * @save_sel: The contents of @sel_reg to be saved over suspend.
+ * @clk_rate: The clock rate from @clk.
+ * @cpu_type: The exact type of this controller.
+ */
 struct s3c2410_nand_info {
        /* mtd info */
        struct nand_hw_control          controller;
@@ -145,12 +168,19 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info)
 
 #define NS_IN_KHZ 1000000
 
+/**
+ * s3c_nand_calc_rate - calculate timing data.
+ * @wanted: The cycle time in nanoseconds.
+ * @clk: The clock rate in kHz.
+ * @max: The maximum divider value.
+ *
+ * Calculate the timing value from the given parameters.
+ */
 static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
 {
        int result;
 
-       result = (wanted * clk) / NS_IN_KHZ;
-       result++;
+       result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ);
 
        pr_debug("result %d from %ld, %d\n", result, clk, wanted);
 
@@ -169,13 +199,21 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
 
 /* controller setup */
 
+/**
+ * s3c2410_nand_setrate - setup controller timing information.
+ * @info: The controller instance.
+ *
+ * Given the information supplied by the platform, calculate and set
+ * the necessary timing registers in the hardware to generate the
+ * necessary timing cycles to the hardware.
+ */
 static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
 {
        struct s3c2410_platform_nand *plat = info->platform;
        int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
        int tacls, twrph0, twrph1;
        unsigned long clkrate = clk_get_rate(info->clk);
-       unsigned long set, cfg, mask;
+       unsigned long uninitialized_var(set), cfg, uninitialized_var(mask);
        unsigned long flags;
 
        /* calculate the timing information for the controller */
@@ -215,9 +253,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
 
        case TYPE_S3C2440:
        case TYPE_S3C2412:
-               mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) |
-                       S3C2410_NFCONF_TWRPH0(7) |
-                       S3C2410_NFCONF_TWRPH1(7));
+               mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) |
+                       S3C2440_NFCONF_TWRPH0(7) |
+                       S3C2440_NFCONF_TWRPH1(7));
 
                set = S3C2440_NFCONF_TACLS(tacls - 1);
                set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
@@ -225,14 +263,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
                break;
 
        default:
-               /* keep compiler happy */
-               mask = 0;
-               set = 0;
                BUG();
        }
 
-       dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
-
        local_irq_save(flags);
 
        cfg = readl(info->regs + S3C2410_NFCONF);
@@ -242,9 +275,18 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
 
        local_irq_restore(flags);
 
+       dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
+
        return 0;
 }
 
+/**
+ * s3c2410_nand_inithw - basic hardware initialisation
+ * @info: The hardware state.
+ *
+ * Do the basic initialisation of the hardware, using s3c2410_nand_setrate()
+ * to setup the hardware access speeds and set the controller to be enabled.
+*/
 static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
 {
        int ret;
@@ -268,8 +310,19 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
        return 0;
 }
 
-/* select chip */
-
+/**
+ * s3c2410_nand_select_chip - select the given nand chip
+ * @mtd: The MTD instance for this chip.
+ * @chip: The chip number.
+ *
+ * This is called by the MTD layer to either select a given chip for the
+ * @mtd instance, or to indicate that the access has finished and the
+ * chip can be de-selected.
+ *
+ * The routine ensures that the nFCE line is correctly setup, and any
+ * platform specific selection code is called to route nFCE to the specific
+ * chip.
+ */
 static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
 {
        struct s3c2410_nand_info *info;
@@ -530,7 +583,16 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 {
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-       readsl(info->regs + S3C2440_NFDATA, buf, len / 4);
+
+       readsl(info->regs + S3C2440_NFDATA, buf, len >> 2);
+
+       /* cleanup if we've got less than a word to do */
+       if (len & 3) {
+               buf += len & ~3;
+
+               for (; len & 3; len--)
+                       *buf++ = readb(info->regs + S3C2440_NFDATA);
+       }
 }
 
 static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
@@ -542,7 +604,16 @@ static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
 static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-       writesl(info->regs + S3C2440_NFDATA, buf, len / 4);
+
+       writesl(info->regs + S3C2440_NFDATA, buf, len >> 2);
+
+       /* cleanup any fractional write */
+       if (len & 3) {
+               buf += len & ~3;
+
+               for (; len & 3; len--, buf++)
+                       writeb(*buf, info->regs + S3C2440_NFDATA);
+       }
 }
 
 /* cpufreq driver support */
@@ -593,7 +664,7 @@ static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *inf
 
 /* device management functions */
 
-static int s3c2410_nand_remove(struct platform_device *pdev)
+static int s3c24xx_nand_remove(struct platform_device *pdev)
 {
        struct s3c2410_nand_info *info = to_nand_info(pdev);
 
@@ -645,17 +716,31 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
 static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
                                      struct s3c2410_nand_mtd *mtd,
                                      struct s3c2410_nand_set *set)
 {
+       struct mtd_partition *part_info;
+       int nr_part = 0;
+
        if (set == NULL)
                return add_mtd_device(&mtd->mtd);
 
-       if (set->nr_partitions > 0 && set->partitions != NULL) {
-               return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
+       if (set->nr_partitions == 0) {
+               mtd->mtd.name = set->name;
+               nr_part = parse_mtd_partitions(&mtd->mtd, part_probes,
+                                               &part_info, 0);
+       } else {
+               if (set->nr_partitions > 0 && set->partitions != NULL) {
+                       nr_part = set->nr_partitions;
+                       part_info = set->partitions;
+               }
        }
 
+       if (nr_part > 0 && part_info)
+               return add_mtd_partitions(&mtd->mtd, part_info, nr_part);
+
        return add_mtd_device(&mtd->mtd);
 }
 #else
@@ -667,11 +752,16 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
 }
 #endif
 
-/* s3c2410_nand_init_chip
+/**
+ * s3c2410_nand_init_chip - initialise a single instance of an chip
+ * @info: The base NAND controller the chip is on.
+ * @nmtd: The new controller MTD instance to fill in.
+ * @set: The information passed from the board specific platform data.
  *
- * init a single instance of an chip
-*/
-
+ * Initialise the given @nmtd from the information in @info and @set. This
+ * readies the structure for use with the MTD layer functions by ensuring
+ * all pointers are setup and the necessary control routines selected.
+ */
 static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
                                   struct s3c2410_nand_mtd *nmtd,
                                   struct s3c2410_nand_set *set)
@@ -757,14 +847,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
 
        if (set->disable_ecc)
                chip->ecc.mode  = NAND_ECC_NONE;
+
+       switch (chip->ecc.mode) {
+       case NAND_ECC_NONE:
+               dev_info(info->device, "NAND ECC disabled\n");
+               break;
+       case NAND_ECC_SOFT:
+               dev_info(info->device, "NAND soft ECC\n");
+               break;
+       case NAND_ECC_HW:
+               dev_info(info->device, "NAND hardware ECC\n");
+               break;
+       default:
+               dev_info(info->device, "NAND ECC UNKNOWN\n");
+               break;
+       }
+
+       /* If you use u-boot BBT creation code, specifying this flag will
+        * let the kernel fish out the BBT from the NAND, and also skip the
+        * full NAND scan that can take 1/2s or so. Little things... */
+       if (set->flash_bbt)
+               chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
 }
 
-/* s3c2410_nand_update_chip
+/**
+ * s3c2410_nand_update_chip - post probe update
+ * @info: The controller instance.
+ * @nmtd: The driver version of the MTD instance.
  *
- * post-probe chip update, to change any items, such as the
- * layout for large page nand
- */
-
+ * This routine is called after the chip probe has succesfully completed
+ * and the relevant per-chip information updated. This call ensure that
+ * we update the internal state accordingly.
+ *
+ * The internal state is currently limited to the ECC state information.
+*/
 static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
                                     struct s3c2410_nand_mtd *nmtd)
 {
@@ -773,33 +889,33 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
        dev_dbg(info->device, "chip %p => page shift %d\n",
                chip, chip->page_shift);
 
-       if (hardware_ecc) {
+       if (chip->ecc.mode != NAND_ECC_HW)
+               return;
+
                /* change the behaviour depending on wether we are using
                 * the large or small page nand device */
 
-               if (chip->page_shift > 10) {
-                       chip->ecc.size      = 256;
-                       chip->ecc.bytes     = 3;
-               } else {
-                       chip->ecc.size      = 512;
-                       chip->ecc.bytes     = 3;
-                       chip->ecc.layout    = &nand_hw_eccoob;
-               }
+       if (chip->page_shift > 10) {
+               chip->ecc.size      = 256;
+               chip->ecc.bytes     = 3;
+       } else {
+               chip->ecc.size      = 512;
+               chip->ecc.bytes     = 3;
+               chip->ecc.layout    = &nand_hw_eccoob;
        }
 }
 
-/* s3c2410_nand_probe
+/* s3c24xx_nand_probe
  *
  * called by device layer when it finds a device matching
  * one our driver can handled. This code checks to see if
  * it can allocate all necessary resources then calls the
  * nand layer to look for devices
 */
-
-static int s3c24xx_nand_probe(struct platform_device *pdev,
-                             enum s3c_cpu_type cpu_type)
+static int s3c24xx_nand_probe(struct platform_device *pdev)
 {
        struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
+       enum s3c_cpu_type cpu_type; 
        struct s3c2410_nand_info *info;
        struct s3c2410_nand_mtd *nmtd;
        struct s3c2410_nand_set *sets;
@@ -809,6 +925,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
        int nr_sets;
        int setno;
 
+       cpu_type = platform_get_device_id(pdev)->driver_data;
+
        pr_debug("s3c2410_nand_probe(%p)\n", pdev);
 
        info = kmalloc(sizeof(*info), GFP_KERNEL);
@@ -922,7 +1040,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
        return 0;
 
  exit_error:
-       s3c2410_nand_remove(pdev);
+       s3c24xx_nand_remove(pdev);
 
        if (err == 0)
                err = -EINVAL;
@@ -983,50 +1101,33 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
 
 /* driver device registration */
 
-static int s3c2410_nand_probe(struct platform_device *dev)
-{
-       return s3c24xx_nand_probe(dev, TYPE_S3C2410);
-}
-
-static int s3c2440_nand_probe(struct platform_device *dev)
-{
-       return s3c24xx_nand_probe(dev, TYPE_S3C2440);
-}
-
-static int s3c2412_nand_probe(struct platform_device *dev)
-{
-       return s3c24xx_nand_probe(dev, TYPE_S3C2412);
-}
-
-static struct platform_driver s3c2410_nand_driver = {
-       .probe          = s3c2410_nand_probe,
-       .remove         = s3c2410_nand_remove,
-       .suspend        = s3c24xx_nand_suspend,
-       .resume         = s3c24xx_nand_resume,
-       .driver         = {
-               .name   = "s3c2410-nand",
-               .owner  = THIS_MODULE,
+static struct platform_device_id s3c24xx_driver_ids[] = {
+       {
+               .name           = "s3c2410-nand",
+               .driver_data    = TYPE_S3C2410,
+       }, {
+               .name           = "s3c2440-nand",
+               .driver_data    = TYPE_S3C2440,
+       }, {
+               .name           = "s3c2412-nand",
+               .driver_data    = TYPE_S3C2412,
+       }, {
+               .name           = "s3c6400-nand",
+               .driver_data    = TYPE_S3C2412, /* compatible with 2412 */
        },
+       { }
 };
 
-static struct platform_driver s3c2440_nand_driver = {
-       .probe          = s3c2440_nand_probe,
-       .remove         = s3c2410_nand_remove,
-       .suspend        = s3c24xx_nand_suspend,
-       .resume         = s3c24xx_nand_resume,
-       .driver         = {
-               .name   = "s3c2440-nand",
-               .owner  = THIS_MODULE,
-       },
-};
+MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
 
-static struct platform_driver s3c2412_nand_driver = {
-       .probe          = s3c2412_nand_probe,
-       .remove         = s3c2410_nand_remove,
+static struct platform_driver s3c24xx_nand_driver = {
+       .probe          = s3c24xx_nand_probe,
+       .remove         = s3c24xx_nand_remove,
        .suspend        = s3c24xx_nand_suspend,
        .resume         = s3c24xx_nand_resume,
+       .id_table       = s3c24xx_driver_ids,
        .driver         = {
-               .name   = "s3c2412-nand",
+               .name   = "s3c24xx-nand",
                .owner  = THIS_MODULE,
        },
 };
@@ -1035,16 +1136,12 @@ static int __init s3c2410_nand_init(void)
 {
        printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
 
-       platform_driver_register(&s3c2412_nand_driver);
-       platform_driver_register(&s3c2440_nand_driver);
-       return platform_driver_register(&s3c2410_nand_driver);
+       return platform_driver_register(&s3c24xx_nand_driver);
 }
 
 static void __exit s3c2410_nand_exit(void)
 {
-       platform_driver_unregister(&s3c2412_nand_driver);
-       platform_driver_unregister(&s3c2440_nand_driver);
-       platform_driver_unregister(&s3c2410_nand_driver);
+       platform_driver_unregister(&s3c24xx_nand_driver);
 }
 
 module_init(s3c2410_nand_init);
@@ -1053,6 +1150,3 @@ module_exit(s3c2410_nand_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
-MODULE_ALIAS("platform:s3c2410-nand");
-MODULE_ALIAS("platform:s3c2412-nand");
-MODULE_ALIAS("platform:s3c2440-nand");
index 812479264896940596ffc36662d8aabe3773d9be..488088eff2cae01ab5b8db8dab1e904d7307da8f 100644 (file)
@@ -64,7 +64,7 @@ struct txx9ndfmc_priv {
        struct nand_chip chip;
        struct mtd_info mtd;
        int cs;
-       char mtdname[BUS_ID_SIZE + 2];
+       const char *mtdname;
 };
 
 #define MAX_TXX9NDFMC_DEV      4
@@ -334,16 +334,23 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
 
                if (plat->ch_mask != 1) {
                        txx9_priv->cs = i;
-                       sprintf(txx9_priv->mtdname, "%s.%u",
-                               dev_name(&dev->dev), i);
+                       txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u",
+                                                      dev_name(&dev->dev), i);
                } else {
                        txx9_priv->cs = -1;
-                       strcpy(txx9_priv->mtdname, dev_name(&dev->dev));
+                       txx9_priv->mtdname = kstrdup(dev_name(&dev->dev),
+                                                    GFP_KERNEL);
+               }
+               if (!txx9_priv->mtdname) {
+                       kfree(txx9_priv);
+                       dev_err(&dev->dev, "Unable to allocate MTD name.\n");
+                       continue;
                }
                if (plat->wide_mask & (1 << i))
                        chip->options |= NAND_BUSWIDTH_16;
 
                if (nand_scan(mtd, 1)) {
+                       kfree(txx9_priv->mtdname);
                        kfree(txx9_priv);
                        continue;
                }
@@ -385,6 +392,7 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
                kfree(drvdata->parts[i]);
 #endif
                del_mtd_device(mtd);
+               kfree(txx9_priv->mtdname);
                kfree(txx9_priv);
        }
        return 0;
index 6391e3dc80025ff243db8391facb4fddf6bf796e..38d656b9b2eecc8f5029bb0b7a6e89f921ff5a2d 100644 (file)
@@ -565,7 +565,7 @@ int omap2_onenand_rephase(void)
                                      NULL, __adjust_timing);
 }
 
-static void __devexit omap2_onenand_shutdown(struct platform_device *pdev)
+static void omap2_onenand_shutdown(struct platform_device *pdev)
 {
        struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
 
@@ -777,7 +777,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
 
 static struct platform_driver omap2_onenand_driver = {
        .probe          = omap2_onenand_probe,
-       .remove         = omap2_onenand_remove,
+       .remove         = __devexit_p(omap2_onenand_remove),
        .shutdown       = omap2_onenand_shutdown,
        .driver         = {
                .name   = DRIVER_NAME,
index 30d6999e5f9f5a75a22078ee56459798ea0fc865..6e829095ea9de8a601f58b2fcb7c4da167e2d746 100644 (file)
@@ -9,6 +9,10 @@
  *     auto-placement support, read-while load support, various fixes
  *     Copyright (C) Nokia Corporation, 2007
  *
+ *     Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
+ *     Flex-OneNAND support
+ *     Copyright (C) Samsung Electronics, 2008
+ *
  * 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.
@@ -16,6 +20,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
 
 #include <asm/io.h>
 
+/* Default Flex-OneNAND boundary and lock respectively */
+static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 };
+
+module_param_array(flex_bdry, int, NULL, 0400);
+MODULE_PARM_DESC(flex_bdry,    "SLC Boundary information for Flex-OneNAND"
+                               "Syntax:flex_bdry=DIE_BDRY,LOCK,..."
+                               "DIE_BDRY: SLC boundary of the die"
+                               "LOCK: Locking information for SLC boundary"
+                               "    : 0->Set boundary in unlocked status"
+                               "    : 1->Set boundary in locked status");
+
+/**
+ *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
+ *  For now, we expose only 64 out of 80 ecc bytes
+ */
+static struct nand_ecclayout onenand_oob_128 = {
+       .eccbytes       = 64,
+       .eccpos         = {
+               6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+               22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+               38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+               54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+               70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+               86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+               102, 103, 104, 105
+               },
+       .oobfree        = {
+               {2, 4}, {18, 4}, {34, 4}, {50, 4},
+               {66, 4}, {82, 4}, {98, 4}, {114, 4}
+       }
+};
+
 /**
  * onenand_oob_64 - oob info for large (2KB) page
  */
@@ -65,6 +102,14 @@ static const unsigned char ffchars[] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */
 };
 
 /**
@@ -170,6 +215,70 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
        return ((bsa << ONENAND_BSA_SHIFT) | bsc);
 }
 
+/**
+ * flexonenand_block- For given address return block number
+ * @param this         - OneNAND device structure
+ * @param addr         - Address for which block number is needed
+ */
+static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr)
+{
+       unsigned boundary, blk, die = 0;
+
+       if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) {
+               die = 1;
+               addr -= this->diesize[0];
+       }
+
+       boundary = this->boundary[die];
+
+       blk = addr >> (this->erase_shift - 1);
+       if (blk > boundary)
+               blk = (blk + boundary + 1) >> 1;
+
+       blk += die ? this->density_mask : 0;
+       return blk;
+}
+
+inline unsigned onenand_block(struct onenand_chip *this, loff_t addr)
+{
+       if (!FLEXONENAND(this))
+               return addr >> this->erase_shift;
+       return flexonenand_block(this, addr);
+}
+
+/**
+ * flexonenand_addr - Return address of the block
+ * @this:              OneNAND device structure
+ * @block:             Block number on Flex-OneNAND
+ *
+ * Return address of the block
+ */
+static loff_t flexonenand_addr(struct onenand_chip *this, int block)
+{
+       loff_t ofs = 0;
+       int die = 0, boundary;
+
+       if (ONENAND_IS_DDP(this) && block >= this->density_mask) {
+               block -= this->density_mask;
+               die = 1;
+               ofs = this->diesize[0];
+       }
+
+       boundary = this->boundary[die];
+       ofs += (loff_t)block << (this->erase_shift - 1);
+       if (block > (boundary + 1))
+               ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1);
+       return ofs;
+}
+
+loff_t onenand_addr(struct onenand_chip *this, int block)
+{
+       if (!FLEXONENAND(this))
+               return (loff_t)block << this->erase_shift;
+       return flexonenand_addr(this, block);
+}
+EXPORT_SYMBOL(onenand_addr);
+
 /**
  * onenand_get_density - [DEFAULT] Get OneNAND density
  * @param dev_id       OneNAND device ID
@@ -182,6 +291,22 @@ static inline int onenand_get_density(int dev_id)
        return (density & ONENAND_DEVICE_DENSITY_MASK);
 }
 
+/**
+ * flexonenand_region - [Flex-OneNAND] Return erase region of addr
+ * @param mtd          MTD device structure
+ * @param addr         address whose erase region needs to be identified
+ */
+int flexonenand_region(struct mtd_info *mtd, loff_t addr)
+{
+       int i;
+
+       for (i = 0; i < mtd->numeraseregions; i++)
+               if (addr < mtd->eraseregions[i].offset)
+                       break;
+       return i - 1;
+}
+EXPORT_SYMBOL(flexonenand_region);
+
 /**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd          MTD device structure
@@ -207,16 +332,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                page = -1;
                break;
 
+       case FLEXONENAND_CMD_PI_ACCESS:
+               /* addr contains die index */
+               block = addr * this->density_mask;
+               page = -1;
+               break;
+
        case ONENAND_CMD_ERASE:
        case ONENAND_CMD_BUFFERRAM:
        case ONENAND_CMD_OTP_ACCESS:
-               block = (int) (addr >> this->erase_shift);
+               block = onenand_block(this, addr);
                page = -1;
                break;
 
+       case FLEXONENAND_CMD_READ_PI:
+               cmd = ONENAND_CMD_READ;
+               block = addr * this->density_mask;
+               page = 0;
+               break;
+
        default:
-               block = (int) (addr >> this->erase_shift);
-               page = (int) (addr >> this->page_shift);
+               block = onenand_block(this, addr);
+               page = (int) (addr - onenand_addr(this, block)) >> this->page_shift;
 
                if (ONENAND_IS_2PLANE(this)) {
                        /* Make the even block number */
@@ -236,7 +373,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                value = onenand_bufferram_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 
-               if (ONENAND_IS_2PLANE(this))
+               if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this))
                        /* It is always BufferRAM0 */
                        ONENAND_SET_BUFFERRAM0(this);
                else
@@ -258,13 +395,18 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
 
        if (page != -1) {
                /* Now we use page size operation */
-               int sectors = 4, count = 4;
+               int sectors = 0, count = 0;
                int dataram;
 
                switch (cmd) {
+               case FLEXONENAND_CMD_RECOVER_LSB:
                case ONENAND_CMD_READ:
                case ONENAND_CMD_READOOB:
-                       dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
+                       if (ONENAND_IS_MLC(this))
+                               /* It is always BufferRAM0 */
+                               dataram = ONENAND_SET_BUFFERRAM0(this);
+                       else
+                               dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
                        break;
 
                default:
@@ -292,6 +434,30 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
        return 0;
 }
 
+/**
+ * onenand_read_ecc - return ecc status
+ * @param this         onenand chip structure
+ */
+static inline int onenand_read_ecc(struct onenand_chip *this)
+{
+       int ecc, i, result = 0;
+
+       if (!FLEXONENAND(this))
+               return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+
+       for (i = 0; i < 4; i++) {
+               ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i);
+               if (likely(!ecc))
+                       continue;
+               if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
+                       return ONENAND_ECC_2BIT_ALL;
+               else
+                       result = ONENAND_ECC_1BIT_ALL;
+       }
+
+       return result;
+}
+
 /**
  * onenand_wait - [DEFAULT] wait until the command is done
  * @param mtd          MTD device structure
@@ -331,14 +497,14 @@ static int onenand_wait(struct mtd_info *mtd, int state)
         * power off recovery (POR) test, it should read ECC status first
         */
        if (interrupt & ONENAND_INT_READ) {
-               int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+               int ecc = onenand_read_ecc(this);
                if (ecc) {
                        if (ecc & ONENAND_ECC_2BIT_ALL) {
                                printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
                                mtd->ecc_stats.failed++;
                                return -EBADMSG;
                        } else if (ecc & ONENAND_ECC_1BIT_ALL) {
-                               printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc);
+                               printk(KERN_DEBUG "onenand_wait: correctable ECC error = 0x%04x\n", ecc);
                                mtd->ecc_stats.corrected++;
                        }
                }
@@ -656,7 +822,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
 
        if (found && ONENAND_IS_DDP(this)) {
                /* Select DataRAM for DDP */
-               int block = (int) (addr >> this->erase_shift);
+               int block = onenand_block(this, addr);
                int value = onenand_bufferram_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
        }
@@ -815,6 +981,149 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
        return 0;
 }
 
+/**
+ * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
+ * @param mtd          MTD device structure
+ * @param addr         address to recover
+ * @param status       return value from onenand_wait / onenand_bbt_wait
+ *
+ * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
+ * lower page address and MSB page has higher page address in paired pages.
+ * If power off occurs during MSB page program, the paired LSB page data can
+ * become corrupt. LSB page recovery read is a way to read LSB page though page
+ * data are corrupted. When uncorrectable error occurs as a result of LSB page
+ * read after power up, issue LSB page recovery read.
+ */
+static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
+{
+       struct onenand_chip *this = mtd->priv;
+       int i;
+
+       /* Recovery is only for Flex-OneNAND */
+       if (!FLEXONENAND(this))
+               return status;
+
+       /* check if we failed due to uncorrectable error */
+       if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR)
+               return status;
+
+       /* check if address lies in MLC region */
+       i = flexonenand_region(mtd, addr);
+       if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
+               return status;
+
+       /* We are attempting to reread, so decrement stats.failed
+        * which was incremented by onenand_wait due to read failure
+        */
+       printk(KERN_INFO "onenand_recover_lsb: Attempting to recover from uncorrectable read\n");
+       mtd->ecc_stats.failed--;
+
+       /* Issue the LSB page recovery command */
+       this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
+       return this->wait(mtd, FL_READING);
+}
+
+/**
+ * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band
+ * @param mtd          MTD device structure
+ * @param from         offset to read from
+ * @param ops:         oob operation description structure
+ *
+ * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram.
+ * So, read-while-load is not present.
+ */
+static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
+                               struct mtd_oob_ops *ops)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct mtd_ecc_stats stats;
+       size_t len = ops->len;
+       size_t ooblen = ops->ooblen;
+       u_char *buf = ops->datbuf;
+       u_char *oobbuf = ops->oobbuf;
+       int read = 0, column, thislen;
+       int oobread = 0, oobcolumn, thisooblen, oobsize;
+       int ret = 0;
+       int writesize = this->writesize;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+       if (ops->mode == MTD_OOB_AUTO)
+               oobsize = this->ecclayout->oobavail;
+       else
+               oobsize = mtd->oobsize;
+
+       oobcolumn = from & (mtd->oobsize - 1);
+
+       /* Do not allow reads past end of device */
+       if (from + len > mtd->size) {
+               printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end of device\n");
+               ops->retlen = 0;
+               ops->oobretlen = 0;
+               return -EINVAL;
+       }
+
+       stats = mtd->ecc_stats;
+
+       while (read < len) {
+               cond_resched();
+
+               thislen = min_t(int, writesize, len - read);
+
+               column = from & (writesize - 1);
+               if (column + thislen > writesize)
+                       thislen = writesize - column;
+
+               if (!onenand_check_bufferram(mtd, from)) {
+                       this->command(mtd, ONENAND_CMD_READ, from, writesize);
+
+                       ret = this->wait(mtd, FL_READING);
+                       if (unlikely(ret))
+                               ret = onenand_recover_lsb(mtd, from, ret);
+                       onenand_update_bufferram(mtd, from, !ret);
+                       if (ret == -EBADMSG)
+                               ret = 0;
+               }
+
+               this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
+               if (oobbuf) {
+                       thisooblen = oobsize - oobcolumn;
+                       thisooblen = min_t(int, thisooblen, ooblen - oobread);
+
+                       if (ops->mode == MTD_OOB_AUTO)
+                               onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
+                       else
+                               this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
+                       oobread += thisooblen;
+                       oobbuf += thisooblen;
+                       oobcolumn = 0;
+               }
+
+               read += thislen;
+               if (read == len)
+                       break;
+
+               from += thislen;
+               buf += thislen;
+       }
+
+       /*
+        * Return success, if no ECC failures, else -EBADMSG
+        * fs driver will take care of that, because
+        * retlen == desired len and result == -EBADMSG
+        */
+       ops->retlen = read;
+       ops->oobretlen = oobread;
+
+       if (ret)
+               return ret;
+
+       if (mtd->ecc_stats.failed - stats.failed)
+               return -EBADMSG;
+
+       return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
 /**
  * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
  * @param mtd          MTD device structure
@@ -962,7 +1271,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
        size_t len = ops->ooblen;
        mtd_oob_mode_t mode = ops->mode;
        u_char *buf = ops->oobbuf;
-       int ret = 0;
+       int ret = 0, readcmd;
 
        from += ops->ooboffs;
 
@@ -993,17 +1302,22 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
 
        stats = mtd->ecc_stats;
 
+       readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
        while (read < len) {
                cond_resched();
 
                thislen = oobsize - column;
                thislen = min_t(int, thislen, len);
 
-               this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+               this->command(mtd, readcmd, from, mtd->oobsize);
 
                onenand_update_bufferram(mtd, from, 0);
 
                ret = this->wait(mtd, FL_READING);
+               if (unlikely(ret))
+                       ret = onenand_recover_lsb(mtd, from, ret);
+
                if (ret && ret != -EBADMSG) {
                        printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
                        break;
@@ -1053,6 +1367,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
 static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
        size_t *retlen, u_char *buf)
 {
+       struct onenand_chip *this = mtd->priv;
        struct mtd_oob_ops ops = {
                .len    = len,
                .ooblen = 0,
@@ -1062,7 +1377,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
        int ret;
 
        onenand_get_device(mtd, FL_READING);
-       ret = onenand_read_ops_nolock(mtd, from, &ops);
+       ret = ONENAND_IS_MLC(this) ?
+               onenand_mlc_read_ops_nolock(mtd, from, &ops) :
+               onenand_read_ops_nolock(mtd, from, &ops);
        onenand_release_device(mtd);
 
        *retlen = ops.retlen;
@@ -1080,6 +1397,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
 static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
                            struct mtd_oob_ops *ops)
 {
+       struct onenand_chip *this = mtd->priv;
        int ret;
 
        switch (ops->mode) {
@@ -1094,7 +1412,9 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
 
        onenand_get_device(mtd, FL_READING);
        if (ops->datbuf)
-               ret = onenand_read_ops_nolock(mtd, from, ops);
+               ret = ONENAND_IS_MLC(this) ?
+                       onenand_mlc_read_ops_nolock(mtd, from, ops) :
+                       onenand_read_ops_nolock(mtd, from, ops);
        else
                ret = onenand_read_oob_nolock(mtd, from, ops);
        onenand_release_device(mtd);
@@ -1128,11 +1448,11 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
        if (interrupt & ONENAND_INT_READ) {
-               int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+               int ecc = onenand_read_ecc(this);
                if (ecc & ONENAND_ECC_2BIT_ALL) {
                        printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x"
                                ", controller error 0x%04x\n", ecc, ctrl);
-                       return ONENAND_BBT_READ_ERROR;
+                       return ONENAND_BBT_READ_ECC_ERROR;
                }
        } else {
                printk(KERN_ERR "onenand_bbt_wait: read timeout!"
@@ -1163,7 +1483,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
 {
        struct onenand_chip *this = mtd->priv;
        int read = 0, thislen, column;
-       int ret = 0;
+       int ret = 0, readcmd;
        size_t len = ops->ooblen;
        u_char *buf = ops->oobbuf;
 
@@ -1183,17 +1503,22 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
 
        column = from & (mtd->oobsize - 1);
 
+       readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
        while (read < len) {
                cond_resched();
 
                thislen = mtd->oobsize - column;
                thislen = min_t(int, thislen, len);
 
-               this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+               this->command(mtd, readcmd, from, mtd->oobsize);
 
                onenand_update_bufferram(mtd, from, 0);
 
-               ret = onenand_bbt_wait(mtd, FL_READING);
+               ret = this->bbt_wait(mtd, FL_READING);
+               if (unlikely(ret))
+                       ret = onenand_recover_lsb(mtd, from, ret);
+
                if (ret)
                        break;
 
@@ -1230,9 +1555,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
 {
        struct onenand_chip *this = mtd->priv;
        u_char *oob_buf = this->oob_buf;
-       int status, i;
+       int status, i, readcmd;
 
-       this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
+       readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
+       this->command(mtd, readcmd, to, mtd->oobsize);
        onenand_update_bufferram(mtd, to, 0);
        status = this->wait(mtd, FL_READING);
        if (status)
@@ -1633,7 +1960,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
 {
        struct onenand_chip *this = mtd->priv;
        int column, ret = 0, oobsize;
-       int written = 0;
+       int written = 0, oobcmd;
        u_char *oobbuf;
        size_t len = ops->ooblen;
        const u_char *buf = ops->oobbuf;
@@ -1675,6 +2002,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
 
        oobbuf = this->oob_buf;
 
+       oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
+
        /* Loop until all data write */
        while (written < len) {
                int thislen = min_t(int, oobsize, len - written);
@@ -1692,7 +2021,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
                        memcpy(oobbuf + column, buf, thislen);
                this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
 
-               this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
+               if (ONENAND_IS_MLC(this)) {
+                       /* Set main area of DataRAM to 0xff*/
+                       memset(this->page_buf, 0xff, mtd->writesize);
+                       this->write_bufferram(mtd, ONENAND_DATARAM,
+                                        this->page_buf, 0, mtd->writesize);
+               }
+
+               this->command(mtd, oobcmd, to, mtd->oobsize);
 
                onenand_update_bufferram(mtd, to, 0);
                if (ONENAND_IS_2PLANE(this)) {
@@ -1815,29 +2151,48 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct onenand_chip *this = mtd->priv;
        unsigned int block_size;
-       loff_t addr;
-       int len;
-       int ret = 0;
+       loff_t addr = instr->addr;
+       loff_t len = instr->len;
+       int ret = 0, i;
+       struct mtd_erase_region_info *region = NULL;
+       loff_t region_end = 0;
 
        DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len);
 
-       block_size = (1 << this->erase_shift);
-
-       /* Start address must align on block boundary */
-       if (unlikely(instr->addr & (block_size - 1))) {
-               printk(KERN_ERR "onenand_erase: Unaligned address\n");
+       /* Do not allow erase past end of device */
+       if (unlikely((len + addr) > mtd->size)) {
+               printk(KERN_ERR "onenand_erase: Erase past end of device\n");
                return -EINVAL;
        }
 
-       /* Length must align on block boundary */
-       if (unlikely(instr->len & (block_size - 1))) {
-               printk(KERN_ERR "onenand_erase: Length not block aligned\n");
-               return -EINVAL;
+       if (FLEXONENAND(this)) {
+               /* Find the eraseregion of this address */
+               i = flexonenand_region(mtd, addr);
+               region = &mtd->eraseregions[i];
+
+               block_size = region->erasesize;
+               region_end = region->offset + region->erasesize * region->numblocks;
+
+               /* Start address within region must align on block boundary.
+                * Erase region's start offset is always block start address.
+                */
+               if (unlikely((addr - region->offset) & (block_size - 1))) {
+                       printk(KERN_ERR "onenand_erase: Unaligned address\n");
+                       return -EINVAL;
+               }
+       } else {
+               block_size = 1 << this->erase_shift;
+
+               /* Start address must align on block boundary */
+               if (unlikely(addr & (block_size - 1))) {
+                       printk(KERN_ERR "onenand_erase: Unaligned address\n");
+                       return -EINVAL;
+               }
        }
 
-       /* Do not allow erase past end of device */
-       if (unlikely((instr->len + instr->addr) > mtd->size)) {
-               printk(KERN_ERR "onenand_erase: Erase past end of device\n");
+       /* Length must align on block boundary */
+       if (unlikely(len & (block_size - 1))) {
+               printk(KERN_ERR "onenand_erase: Length not block aligned\n");
                return -EINVAL;
        }
 
@@ -1847,9 +2202,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
        onenand_get_device(mtd, FL_ERASING);
 
        /* Loop throught the pages */
-       len = instr->len;
-       addr = instr->addr;
-
        instr->state = MTD_ERASING;
 
        while (len) {
@@ -1869,7 +2221,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
                ret = this->wait(mtd, FL_ERASING);
                /* Check, if it is write protected */
                if (ret) {
-                       printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
+                       printk(KERN_ERR "onenand_erase: Failed erase, block %d\n",
+                                                onenand_block(this, addr));
                        instr->state = MTD_ERASE_FAILED;
                        instr->fail_addr = addr;
                        goto erase_exit;
@@ -1877,6 +2230,22 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 
                len -= block_size;
                addr += block_size;
+
+               if (addr == region_end) {
+                       if (!len)
+                               break;
+                       region++;
+
+                       block_size = region->erasesize;
+                       region_end = region->offset + region->erasesize * region->numblocks;
+
+                       if (len & (block_size - 1)) {
+                               /* FIXME: This should be handled at MTD partitioning level. */
+                               printk(KERN_ERR "onenand_erase: Unaligned address\n");
+                               goto erase_exit;
+                       }
+               }
+
        }
 
        instr->state = MTD_ERASE_DONE;
@@ -1955,13 +2324,17 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
        int block;
 
        /* Get block number */
-       block = ((int) ofs) >> bbm->bbt_erase_shift;
+       block = onenand_block(this, ofs);
         if (bbm->bbt)
                 bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
 
         /* We write two bytes, so we dont have to mess with 16 bit access */
         ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
-        return onenand_write_oob_nolock(mtd, ofs, &ops);
+       /* FIXME : What to do when marking SLC block in partition
+        *         with MLC erasesize? For now, it is not advisable to
+        *         create partitions containing both SLC and MLC regions.
+        */
+       return onenand_write_oob_nolock(mtd, ofs, &ops);
 }
 
 /**
@@ -2005,8 +2378,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
        int start, end, block, value, status;
        int wp_status_mask;
 
-       start = ofs >> this->erase_shift;
-       end = len >> this->erase_shift;
+       start = onenand_block(this, ofs);
+       end = onenand_block(this, ofs + len) - 1;
 
        if (cmd == ONENAND_CMD_LOCK)
                wp_status_mask = ONENAND_WP_LS;
@@ -2018,7 +2391,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
                /* Set start block address */
                this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
                /* Set end block address */
-               this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
+               this->write_word(end, this->base +  ONENAND_REG_END_BLOCK_ADDRESS);
                /* Write lock command */
                this->command(mtd, cmd, 0, 0);
 
@@ -2039,7 +2412,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
        }
 
        /* Block lock scheme */
-       for (block = start; block < start + end; block++) {
+       for (block = start; block < end + 1; block++) {
                /* Set block address */
                value = onenand_block_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
@@ -2147,7 +2520,7 @@ static void onenand_unlock_all(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
        loff_t ofs = 0;
-       size_t len = this->chipsize;
+       loff_t len = mtd->size;
 
        if (this->options & ONENAND_HAS_UNLOCK_ALL) {
                /* Set start block address */
@@ -2163,12 +2536,16 @@ static void onenand_unlock_all(struct mtd_info *mtd)
                    & ONENAND_CTRL_ONGO)
                        continue;
 
+               /* Don't check lock status */
+               if (this->options & ONENAND_SKIP_UNLOCK_CHECK)
+                       return;
+
                /* Check lock status */
                if (onenand_check_lock_status(this))
                        return;
 
                /* Workaround for all block unlock in DDP */
-               if (ONENAND_IS_DDP(this)) {
+               if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) {
                        /* All blocks on another chip */
                        ofs = this->chipsize >> 1;
                        len = this->chipsize >> 1;
@@ -2210,7 +2587,9 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
        this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
        this->wait(mtd, FL_OTPING);
 
-       ret = onenand_read_ops_nolock(mtd, from, &ops);
+       ret = ONENAND_IS_MLC(this) ?
+               onenand_mlc_read_ops_nolock(mtd, from, &ops) :
+               onenand_read_ops_nolock(mtd, from, &ops);
 
        /* Exit OTP access mode */
        this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2277,21 +2656,32 @@ 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;
-       struct mtd_oob_ops ops = {
-               .mode = MTD_OOB_PLACE,
-               .ooblen = len,
-               .oobbuf = buf,
-               .ooboffs = 0,
-       };
+       struct mtd_oob_ops ops;
        int ret;
 
        /* Enter OTP access mode */
        this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
        this->wait(mtd, FL_OTPING);
 
-       ret = onenand_write_oob_nolock(mtd, from, &ops);
-
-       *retlen = ops.oobretlen;
+       if (FLEXONENAND(this)) {
+               /*
+                * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
+                * main area of page 49.
+                */
+               ops.len = mtd->writesize;
+               ops.ooblen = 0;
+               ops.datbuf = buf;
+               ops.oobbuf = NULL;
+               ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
+               *retlen = ops.retlen;
+       } else {
+               ops.mode = MTD_OOB_PLACE;
+               ops.ooblen = len;
+               ops.oobbuf = buf;
+               ops.ooboffs = 0;
+               ret = onenand_write_oob_nolock(mtd, from, &ops);
+               *retlen = ops.oobretlen;
+       }
 
        /* Exit OTP access mode */
        this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2475,27 +2865,34 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
                        size_t len)
 {
        struct onenand_chip *this = mtd->priv;
-       u_char *oob_buf = this->oob_buf;
+       u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
        size_t retlen;
        int ret;
 
-       memset(oob_buf, 0xff, mtd->oobsize);
+       memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
+                                                : 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;
+       if (FLEXONENAND(this))
+               buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
+       else
+               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.
+        * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
+        * main area of page 49.
         */
+
        from = 0;
-       len = 16;
+       len = FLEXONENAND(this) ? mtd->writesize : 16;
 
-       ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER);
+       ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
 
        return ret ? : retlen;
 }
@@ -2542,6 +2939,14 @@ static void onenand_check_features(struct mtd_info *mtd)
                break;
        }
 
+       if (ONENAND_IS_MLC(this))
+               this->options &= ~ONENAND_HAS_2PLANE;
+
+       if (FLEXONENAND(this)) {
+               this->options &= ~ONENAND_HAS_CONT_LOCK;
+               this->options |= ONENAND_HAS_UNLOCK_ALL;
+       }
+
        if (this->options & ONENAND_HAS_CONT_LOCK)
                printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
        if (this->options & ONENAND_HAS_UNLOCK_ALL)
@@ -2559,14 +2964,16 @@ static void onenand_check_features(struct mtd_info *mtd)
  */
 static void onenand_print_device_info(int device, int version)
 {
-        int vcc, demuxed, ddp, density;
+       int vcc, demuxed, ddp, density, flexonenand;
 
         vcc = device & ONENAND_DEVICE_VCC_MASK;
         demuxed = device & ONENAND_DEVICE_IS_DEMUX;
         ddp = device & ONENAND_DEVICE_IS_DDP;
         density = onenand_get_density(device);
-        printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
-                demuxed ? "" : "Muxed ",
+       flexonenand = device & DEVICE_IS_FLEXONENAND;
+       printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
+               demuxed ? "" : "Muxed ",
+               flexonenand ? "Flex-" : "",
                 ddp ? "(DDP)" : "",
                 (16 << density),
                 vcc ? "2.65/3.3" : "1.8",
@@ -2576,6 +2983,7 @@ static void onenand_print_device_info(int device, int version)
 
 static const struct onenand_manufacturers onenand_manuf_ids[] = {
         {ONENAND_MFR_SAMSUNG, "Samsung"},
+       {ONENAND_MFR_NUMONYX, "Numonyx"},
 };
 
 /**
@@ -2604,6 +3012,261 @@ static int onenand_check_maf(int manuf)
        return (i == size);
 }
 
+/**
+* flexonenand_get_boundary     - Reads the SLC boundary
+* @param onenand_info          - onenand info structure
+**/
+static int flexonenand_get_boundary(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned die, bdry;
+       int ret, syscfg, locked;
+
+       /* Disable ECC */
+       syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+       this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
+
+       for (die = 0; die < this->dies; die++) {
+               this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
+               this->wait(mtd, FL_SYNCING);
+
+               this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
+               ret = this->wait(mtd, FL_READING);
+
+               bdry = this->read_word(this->base + ONENAND_DATARAM);
+               if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
+                       locked = 0;
+               else
+                       locked = 1;
+               this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
+
+               this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+               ret = this->wait(mtd, FL_RESETING);
+
+               printk(KERN_INFO "Die %d boundary: %d%s\n", die,
+                      this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
+       }
+
+       /* Enable ECC */
+       this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+       return 0;
+}
+
+/**
+ * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
+ *                       boundary[], diesize[], mtd->size, mtd->erasesize
+ * @param mtd          - MTD device structure
+ */
+static void flexonenand_get_size(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       int die, i, eraseshift, density;
+       int blksperdie, maxbdry;
+       loff_t ofs;
+
+       density = onenand_get_density(this->device_id);
+       blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift);
+       blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
+       maxbdry = blksperdie - 1;
+       eraseshift = this->erase_shift - 1;
+
+       mtd->numeraseregions = this->dies << 1;
+
+       /* This fills up the device boundary */
+       flexonenand_get_boundary(mtd);
+       die = ofs = 0;
+       i = -1;
+       for (; die < this->dies; die++) {
+               if (!die || this->boundary[die-1] != maxbdry) {
+                       i++;
+                       mtd->eraseregions[i].offset = ofs;
+                       mtd->eraseregions[i].erasesize = 1 << eraseshift;
+                       mtd->eraseregions[i].numblocks =
+                                                       this->boundary[die] + 1;
+                       ofs += mtd->eraseregions[i].numblocks << eraseshift;
+                       eraseshift++;
+               } else {
+                       mtd->numeraseregions -= 1;
+                       mtd->eraseregions[i].numblocks +=
+                                                       this->boundary[die] + 1;
+                       ofs += (this->boundary[die] + 1) << (eraseshift - 1);
+               }
+               if (this->boundary[die] != maxbdry) {
+                       i++;
+                       mtd->eraseregions[i].offset = ofs;
+                       mtd->eraseregions[i].erasesize = 1 << eraseshift;
+                       mtd->eraseregions[i].numblocks = maxbdry ^
+                                                        this->boundary[die];
+                       ofs += mtd->eraseregions[i].numblocks << eraseshift;
+                       eraseshift--;
+               } else
+                       mtd->numeraseregions -= 1;
+       }
+
+       /* Expose MLC erase size except when all blocks are SLC */
+       mtd->erasesize = 1 << this->erase_shift;
+       if (mtd->numeraseregions == 1)
+               mtd->erasesize >>= 1;
+
+       printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions);
+       for (i = 0; i < mtd->numeraseregions; i++)
+               printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x,"
+                       " numblocks: %04u]\n",
+                       (unsigned int) mtd->eraseregions[i].offset,
+                       mtd->eraseregions[i].erasesize,
+                       mtd->eraseregions[i].numblocks);
+
+       for (die = 0, mtd->size = 0; die < this->dies; die++) {
+               this->diesize[die] = (loff_t)blksperdie << this->erase_shift;
+               this->diesize[die] -= (loff_t)(this->boundary[die] + 1)
+                                                << (this->erase_shift - 1);
+               mtd->size += this->diesize[die];
+       }
+}
+
+/**
+ * flexonenand_check_blocks_erased - Check if blocks are erased
+ * @param mtd_info     - mtd info structure
+ * @param start                - first erase block to check
+ * @param end          - last erase block to check
+ *
+ * Converting an unerased block from MLC to SLC
+ * causes byte values to change. Since both data and its ECC
+ * have changed, reads on the block give uncorrectable error.
+ * This might lead to the block being detected as bad.
+ *
+ * Avoid this by ensuring that the block to be converted is
+ * erased.
+ */
+static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end)
+{
+       struct onenand_chip *this = mtd->priv;
+       int i, ret;
+       int block;
+       struct mtd_oob_ops ops = {
+               .mode = MTD_OOB_PLACE,
+               .ooboffs = 0,
+               .ooblen = mtd->oobsize,
+               .datbuf = NULL,
+               .oobbuf = this->oob_buf,
+       };
+       loff_t addr;
+
+       printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end);
+
+       for (block = start; block <= end; block++) {
+               addr = flexonenand_addr(this, block);
+               if (onenand_block_isbad_nolock(mtd, addr, 0))
+                       continue;
+
+               /*
+                * Since main area write results in ECC write to spare,
+                * it is sufficient to check only ECC bytes for change.
+                */
+               ret = onenand_read_oob_nolock(mtd, addr, &ops);
+               if (ret)
+                       return ret;
+
+               for (i = 0; i < mtd->oobsize; i++)
+                       if (this->oob_buf[i] != 0xff)
+                               break;
+
+               if (i != mtd->oobsize) {
+                       printk(KERN_WARNING "Block %d not erased.\n", block);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * flexonenand_set_boundary    - Writes the SLC boundary
+ * @param mtd                  - mtd info structure
+ */
+int flexonenand_set_boundary(struct mtd_info *mtd, int die,
+                                   int boundary, int lock)
+{
+       struct onenand_chip *this = mtd->priv;
+       int ret, density, blksperdie, old, new, thisboundary;
+       loff_t addr;
+
+       /* Change only once for SDP Flex-OneNAND */
+       if (die && (!ONENAND_IS_DDP(this)))
+               return 0;
+
+       /* boundary value of -1 indicates no required change */
+       if (boundary < 0 || boundary == this->boundary[die])
+               return 0;
+
+       density = onenand_get_density(this->device_id);
+       blksperdie = ((16 << density) << 20) >> this->erase_shift;
+       blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
+
+       if (boundary >= blksperdie) {
+               printk(KERN_ERR "flexonenand_set_boundary: Invalid boundary value. "
+                               "Boundary not changed.\n");
+               return -EINVAL;
+       }
+
+       /* Check if converting blocks are erased */
+       old = this->boundary[die] + (die * this->density_mask);
+       new = boundary + (die * this->density_mask);
+       ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new));
+       if (ret) {
+               printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n");
+               return ret;
+       }
+
+       this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
+       this->wait(mtd, FL_SYNCING);
+
+       /* Check is boundary is locked */
+       this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
+       ret = this->wait(mtd, FL_READING);
+
+       thisboundary = this->read_word(this->base + ONENAND_DATARAM);
+       if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) {
+               printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n");
+               ret = 1;
+               goto out;
+       }
+
+       printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n",
+                       die, boundary, lock ? "(Locked)" : "(Unlocked)");
+
+       addr = die ? this->diesize[0] : 0;
+
+       boundary &= FLEXONENAND_PI_MASK;
+       boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
+
+       this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
+       ret = this->wait(mtd, FL_ERASING);
+       if (ret) {
+               printk(KERN_ERR "flexonenand_set_boundary: Failed PI erase for Die %d\n", die);
+               goto out;
+       }
+
+       this->write_word(boundary, this->base + ONENAND_DATARAM);
+       this->command(mtd, ONENAND_CMD_PROG, addr, 0);
+       ret = this->wait(mtd, FL_WRITING);
+       if (ret) {
+               printk(KERN_ERR "flexonenand_set_boundary: Failed PI write for Die %d\n", die);
+               goto out;
+       }
+
+       this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0);
+       ret = this->wait(mtd, FL_WRITING);
+out:
+       this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND);
+       this->wait(mtd, FL_RESETING);
+       if (!ret)
+               /* Recalculate device size on boundary change*/
+               flexonenand_get_size(mtd);
+
+       return ret;
+}
+
 /**
  * onenand_probe - [OneNAND Interface] Probe the OneNAND device
  * @param mtd          MTD device structure
@@ -2621,7 +3284,7 @@ static int onenand_probe(struct mtd_info *mtd)
        /* Save system configuration 1 */
        syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
        /* Clear Sync. Burst Read mode to read BootRAM */
-       this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);
+       this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1);
 
        /* Send the command for reading device ID from BootRAM */
        this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
@@ -2646,6 +3309,7 @@ static int onenand_probe(struct mtd_info *mtd)
        maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
        dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
        ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
+       this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
 
        /* Check OneNAND device */
        if (maf_id != bram_maf_id || dev_id != bram_dev_id)
@@ -2657,29 +3321,55 @@ static int onenand_probe(struct mtd_info *mtd)
        this->version_id = ver_id;
 
        density = onenand_get_density(dev_id);
+       if (FLEXONENAND(this)) {
+               this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
+               /* Maximum possible erase regions */
+               mtd->numeraseregions = this->dies << 1;
+               mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info)
+                                       * (this->dies << 1), GFP_KERNEL);
+               if (!mtd->eraseregions)
+                       return -ENOMEM;
+       }
+
+       /*
+        * For Flex-OneNAND, chipsize represents maximum possible device size.
+        * mtd->size represents the actual device size.
+        */
        this->chipsize = (16 << density) << 20;
-       /* Set density mask. it is used for DDP */
-       if (ONENAND_IS_DDP(this))
-               this->density_mask = (1 << (density + 6));
-       else
-               this->density_mask = 0;
 
        /* OneNAND page size & block size */
        /* The data buffer size is equal to page size */
        mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
+       /* We use the full BufferRAM */
+       if (ONENAND_IS_MLC(this))
+               mtd->writesize <<= 1;
+
        mtd->oobsize = mtd->writesize >> 5;
        /* Pages per a block are always 64 in OneNAND */
        mtd->erasesize = mtd->writesize << 6;
+       /*
+        * Flex-OneNAND SLC area has 64 pages per block.
+        * Flex-OneNAND MLC area has 128 pages per block.
+        * Expose MLC erase size to find erase_shift and page_mask.
+        */
+       if (FLEXONENAND(this))
+               mtd->erasesize <<= 1;
 
        this->erase_shift = ffs(mtd->erasesize) - 1;
        this->page_shift = ffs(mtd->writesize) - 1;
        this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
+       /* Set density mask. it is used for DDP */
+       if (ONENAND_IS_DDP(this))
+               this->density_mask = this->chipsize >> (this->erase_shift + 1);
        /* It's real page size */
        this->writesize = mtd->writesize;
 
        /* REVIST: Multichip handling */
 
-       mtd->size = this->chipsize;
+       if (FLEXONENAND(this))
+               flexonenand_get_size(mtd);
+       else
+               mtd->size = this->chipsize;
 
        /* Check OneNAND features */
        onenand_check_features(mtd);
@@ -2734,7 +3424,7 @@ static void onenand_resume(struct mtd_info *mtd)
  */
 int onenand_scan(struct mtd_info *mtd, int maxchips)
 {
-       int i;
+       int i, ret;
        struct onenand_chip *this = mtd->priv;
 
        if (!this->read_word)
@@ -2746,6 +3436,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
                this->command = onenand_command;
        if (!this->wait)
                onenand_setup_wait(mtd);
+       if (!this->bbt_wait)
+               this->bbt_wait = onenand_bbt_wait;
+       if (!this->unlock_all)
+               this->unlock_all = onenand_unlock_all;
 
        if (!this->read_bufferram)
                this->read_bufferram = onenand_read_bufferram;
@@ -2796,6 +3490,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
         * Allow subpage writes up to oobsize.
         */
        switch (mtd->oobsize) {
+       case 128:
+               this->ecclayout = &onenand_oob_128;
+               mtd->subpage_sft = 0;
+               break;
        case 64:
                this->ecclayout = &onenand_oob_64;
                mtd->subpage_sft = 2;
@@ -2859,9 +3557,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        mtd->owner = THIS_MODULE;
 
        /* Unlock whole block */
-       onenand_unlock_all(mtd);
+       this->unlock_all(mtd);
+
+       ret = this->scan_bbt(mtd);
+       if ((!FLEXONENAND(this)) || ret)
+               return ret;
 
-       return this->scan_bbt(mtd);
+       /* Change Flex-OneNAND boundaries if required */
+       for (i = 0; i < MAX_DIES; i++)
+               flexonenand_set_boundary(mtd, i, flex_bdry[2 * i],
+                                                flex_bdry[(2 * i) + 1]);
+
+       return 0;
 }
 
 /**
@@ -2890,6 +3597,7 @@ void onenand_release(struct mtd_info *mtd)
                kfree(this->page_buf);
        if (this->options & ONENAND_OOBBUF_ALLOC)
                kfree(this->oob_buf);
+       kfree(mtd->eraseregions);
 }
 
 EXPORT_SYMBOL_GPL(onenand_scan);
index 2f53b51c68054cb5a11aa17bf9646c56c7d975e3..a91fcac1af01c6cf0c4f1b20cb41836308ab5e17 100644 (file)
@@ -63,6 +63,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
        loff_t from;
        size_t readlen, ooblen;
        struct mtd_oob_ops ops;
+       int rgn;
 
        printk(KERN_INFO "Scanning device for bad blocks\n");
 
@@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
        /* Note that numblocks is 2 * (real numblocks) here;
         * see i += 2 below as it makses shifting and masking less painful
         */
-       numblocks = mtd->size >> (bbm->bbt_erase_shift - 1);
+       numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
        startblock = 0;
        from = 0;
 
@@ -106,7 +107,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
                        }
                }
                i += 2;
-               from += (1 << bbm->bbt_erase_shift);
+
+               if (FLEXONENAND(this)) {
+                       rgn = flexonenand_region(mtd, from);
+                       from += mtd->eraseregions[rgn].erasesize;
+               } else
+                       from += (1 << bbm->bbt_erase_shift);
        }
 
        return 0;
@@ -143,7 +149,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
        uint8_t res;
 
        /* Get block number * 2 */
-       block = (int) (offs >> (bbm->bbt_erase_shift - 1));
+       block = (int) (onenand_block(this, offs) << 1);
        res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
 
        DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
@@ -178,7 +184,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
        struct bbm_info *bbm = this->bbm;
        int len, ret = 0;
 
-       len = mtd->size >> (this->erase_shift + 2);
+       len = this->chipsize >> (this->erase_shift + 2);
        /* Allocate memory (2bit per block) and clear the memory bad block table */
        bbm->bbt = kzalloc(len, GFP_KERNEL);
        if (!bbm->bbt) {
index d64200b7c94bfd4c0caf5dc782ba2146d0f584f9..f6e3c8aebd3a107b43c711a05fdebcdc283e05f8 100644 (file)
@@ -6,6 +6,10 @@
  *  Copyright Â© 2005-2007 Samsung Electronics
  *  Kyungmin Park <kyungmin.park@samsung.com>
  *
+ *  Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
+ *  Flex-OneNAND simulator support
+ *  Copyright (C) Samsung Electronics, 2008
+ *
  * 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.
 #ifndef CONFIG_ONENAND_SIM_MANUFACTURER
 #define CONFIG_ONENAND_SIM_MANUFACTURER         0xec
 #endif
+
 #ifndef CONFIG_ONENAND_SIM_DEVICE_ID
 #define CONFIG_ONENAND_SIM_DEVICE_ID            0x04
 #endif
+
+#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1)
+
 #ifndef CONFIG_ONENAND_SIM_VERSION_ID
 #define CONFIG_ONENAND_SIM_VERSION_ID           0x1e
 #endif
 
+#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID
+#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND
+#endif
+
+/* Initial boundary values for Flex-OneNAND Simulator */
+#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY
+#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY   0x01
+#endif
+
+#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY
+#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY   0x01
+#endif
+
 static int manuf_id    = CONFIG_ONENAND_SIM_MANUFACTURER;
 static int device_id   = CONFIG_ONENAND_SIM_DEVICE_ID;
 static int version_id  = CONFIG_ONENAND_SIM_VERSION_ID;
+static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID;
+static int boundary[] = {
+       CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY,
+       CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY,
+};
 
 struct onenand_flash {
        void __iomem *base;
@@ -57,12 +83,18 @@ struct onenand_flash {
        (writew(v, this->base + ONENAND_REG_WP_STATUS))
 
 /* It has all 0xff chars */
-#define MAX_ONENAND_PAGESIZE           (2048 + 64)
+#define MAX_ONENAND_PAGESIZE           (4096 + 128)
 static unsigned char *ffchars;
 
+#if CONFIG_FLEXONENAND
+#define PARTITION_NAME "Flex-OneNAND simulator partition"
+#else
+#define PARTITION_NAME "OneNAND simulator partition"
+#endif
+
 static struct mtd_partition os_partitions[] = {
        {
-               .name           = "OneNAND simulator partition",
+               .name           = PARTITION_NAME,
                .offset         = 0,
                .size           = MTDPART_SIZ_FULL,
        },
@@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip *this, int cmd)
 
        switch (cmd) {
        case ONENAND_CMD_UNLOCK:
+       case ONENAND_CMD_UNLOCK_ALL:
                if (block_lock_scheme)
                        ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
                else
@@ -228,10 +261,12 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
 {
        struct mtd_info *mtd = &info->mtd;
        struct onenand_flash *flash = this->priv;
-       int main_offset, spare_offset;
+       int main_offset, spare_offset, die = 0;
        void __iomem *src;
        void __iomem *dest;
        unsigned int i;
+       static int pi_operation;
+       int erasesize, rgn;
 
        if (dataram) {
                main_offset = mtd->writesize;
@@ -241,10 +276,27 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
                spare_offset = 0;
        }
 
+       if (pi_operation) {
+               die = readw(this->base + ONENAND_REG_START_ADDRESS2);
+               die >>= ONENAND_DDP_SHIFT;
+       }
+
        switch (cmd) {
+       case FLEXONENAND_CMD_PI_ACCESS:
+               pi_operation = 1;
+               break;
+
+       case ONENAND_CMD_RESET:
+               pi_operation = 0;
+               break;
+
        case ONENAND_CMD_READ:
                src = ONENAND_CORE(flash) + offset;
                dest = ONENAND_MAIN_AREA(this, main_offset);
+               if (pi_operation) {
+                       writew(boundary[die], this->base + ONENAND_DATARAM);
+                       break;
+               }
                memcpy(dest, src, mtd->writesize);
                /* Fall through */
 
@@ -257,6 +309,10 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
        case ONENAND_CMD_PROG:
                src = ONENAND_MAIN_AREA(this, main_offset);
                dest = ONENAND_CORE(flash) + offset;
+               if (pi_operation) {
+                       boundary[die] = readw(this->base + ONENAND_DATARAM);
+                       break;
+               }
                /* To handle partial write */
                for (i = 0; i < (1 << mtd->subpage_sft); i++) {
                        int off = i * this->subpagesize;
@@ -284,9 +340,18 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
                break;
 
        case ONENAND_CMD_ERASE:
-               memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
+               if (pi_operation)
+                       break;
+
+               if (FLEXONENAND(this)) {
+                       rgn = flexonenand_region(mtd, offset);
+                       erasesize = mtd->eraseregions[rgn].erasesize;
+               } else
+                       erasesize = mtd->erasesize;
+
+               memset(ONENAND_CORE(flash) + offset, 0xff, erasesize);
                memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
-                      (mtd->erasesize >> 5));
+                      (erasesize >> 5));
                break;
 
        default:
@@ -339,7 +404,7 @@ static void onenand_command_handle(struct onenand_chip *this, int cmd)
        }
 
        if (block != -1)
-               offset += block << this->erase_shift;
+               offset = onenand_addr(this, block);
 
        if (page != -1)
                offset += page << this->page_shift;
@@ -390,6 +455,7 @@ static int __init flash_init(struct onenand_flash *flash)
        }
 
        density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       density &= ONENAND_DEVICE_DENSITY_MASK;
        size = ((16 << 20) << density);
 
        ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
@@ -405,8 +471,9 @@ static int __init flash_init(struct onenand_flash *flash)
        writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
        writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
        writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
+       writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY);
 
-       if (density < 2)
+       if (density < 2 && (!CONFIG_FLEXONENAND))
                buffer_size = 0x0400;   /* 1KiB page */
        else
                buffer_size = 0x0800;   /* 2KiB page */
index c5ded5ff72b58b5c45e5e7cb1b5cd4d639dc4567..c135202c38b3667b30698114d13d8f49cc076ab2 100644 (file)
@@ -94,7 +94,6 @@
 #include <linux/atm_tcp.h>
 #include <linux/sonet.h>
 #include <linux/atm_suni.h>
-#include <linux/mtd/mtd.h>
 
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
@@ -1405,46 +1404,6 @@ static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg)
 #define HIDPGETCONNLIST        _IOR('H', 210, int)
 #define HIDPGETCONNINFO        _IOR('H', 211, int)
 
-struct mtd_oob_buf32 {
-       u_int32_t start;
-       u_int32_t length;
-       compat_caddr_t ptr;     /* unsigned char* */
-};
-
-#define MEMWRITEOOB32  _IOWR('M',3,struct mtd_oob_buf32)
-#define MEMREADOOB32   _IOWR('M',4,struct mtd_oob_buf32)
-
-static int mtd_rw_oob(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
-       struct mtd_oob_buf __user *buf = compat_alloc_user_space(sizeof(*buf));
-       struct mtd_oob_buf32 __user *buf32 = compat_ptr(arg);
-       u32 data;
-       char __user *datap;
-       unsigned int real_cmd;
-       int err;
-
-       real_cmd = (cmd == MEMREADOOB32) ?
-               MEMREADOOB : MEMWRITEOOB;
-
-       if (copy_in_user(&buf->start, &buf32->start,
-                        2 * sizeof(u32)) ||
-           get_user(data, &buf32->ptr))
-               return -EFAULT;
-       datap = compat_ptr(data);
-       if (put_user(datap, &buf->ptr))
-               return -EFAULT;
-
-       err = sys_ioctl(fd, real_cmd, (unsigned long) buf);
-
-       if (!err) {
-               if (copy_in_user(&buf32->start, &buf->start,
-                                2 * sizeof(u32)))
-                       err = -EFAULT;
-       }
-
-       return err;
-}      
-
 #ifdef CONFIG_BLOCK
 struct raw32_config_request
 {
@@ -2426,15 +2385,6 @@ COMPATIBLE_IOCTL(USBDEVFS_SUBMITURB32)
 COMPATIBLE_IOCTL(USBDEVFS_REAPURB32)
 COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32)
 COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT)
-/* MTD */
-COMPATIBLE_IOCTL(MEMGETINFO)
-COMPATIBLE_IOCTL(MEMERASE)
-COMPATIBLE_IOCTL(MEMLOCK)
-COMPATIBLE_IOCTL(MEMUNLOCK)
-COMPATIBLE_IOCTL(MEMGETREGIONCOUNT)
-COMPATIBLE_IOCTL(MEMGETREGIONINFO)
-COMPATIBLE_IOCTL(MEMGETBADBLOCK)
-COMPATIBLE_IOCTL(MEMSETBADBLOCK)
 /* NBD */
 ULONG_IOCTL(NBD_SET_SOCK)
 ULONG_IOCTL(NBD_SET_BLKSIZE)
@@ -2544,8 +2494,6 @@ COMPATIBLE_IOCTL(JSIOCGBUTTONS)
 COMPATIBLE_IOCTL(JSIOCGNAME(0))
 
 /* now things that need handlers */
-HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob)
-HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob)
 #ifdef CONFIG_NET
 HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32)
 HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf)
index 1d437de1e9a8c1fa1aed3992571f8ed7c2e9bf7b..7515e73e2bfbd0729069b0ec0f16df2da74f71a6 100644 (file)
@@ -196,7 +196,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
                                if (c->nextblock) {
                                        ret = file_dirty(c, c->nextblock);
                                        if (ret)
-                                               return ret;
+                                               goto out;
                                        /* deleting summary information of the old nextblock */
                                        jffs2_sum_reset_collected(c->summary);
                                }
@@ -207,7 +207,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
                        } else {
                                ret = file_dirty(c, jeb);
                                if (ret)
-                                       return ret;
+                                       goto out;
                        }
                        break;
 
index 7efb9be34662b4aeb905adb246e366b2f9a1a58f..4030ebada49e922cbdc8fb8e486a7a2b149d177d 100644 (file)
@@ -563,6 +563,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
  * @options:           Option flags, e.g. 16bit buswidth
  * @ecclayout:         ecc layout info structure
  * @part_probe_types:  NULL-terminated array of probe types
+ * @set_parts:         platform specific function to set partitions
  * @priv:              hardware controller specific settings
  */
 struct platform_nand_chip {
@@ -574,26 +575,41 @@ struct platform_nand_chip {
        int                     chip_delay;
        unsigned int            options;
        const char              **part_probe_types;
+       void                    (*set_parts)(uint64_t size,
+                                       struct platform_nand_chip *chip);
        void                    *priv;
 };
 
+/* Keep gcc happy */
+struct platform_device;
+
 /**
  * struct platform_nand_ctrl - controller level device structure
+ * @probe:             platform specific function to probe/setup hardware
+ * @remove:            platform specific function to remove/teardown hardware
  * @hwcontrol:         platform specific hardware control structure
  * @dev_ready:         platform specific function to read ready/busy pin
  * @select_chip:       platform specific chip select function
  * @cmd_ctrl:          platform specific function for controlling
  *                     ALE/CLE/nCE. Also used to write command and address
+ * @write_buf:         platform specific function for write buffer
+ * @read_buf:          platform specific function for read buffer
  * @priv:              private data to transport driver specific settings
  *
  * All fields are optional and depend on the hardware driver requirements
  */
 struct platform_nand_ctrl {
+       int             (*probe)(struct platform_device *pdev);
+       void            (*remove)(struct platform_device *pdev);
        void            (*hwcontrol)(struct mtd_info *mtd, int cmd);
        int             (*dev_ready)(struct mtd_info *mtd);
        void            (*select_chip)(struct mtd_info *mtd, int chip);
        void            (*cmd_ctrl)(struct mtd_info *mtd, int dat,
                                    unsigned int ctrl);
+       void            (*write_buf)(struct mtd_info *mtd,
+                                   const uint8_t *buf, int len);
+       void            (*read_buf)(struct mtd_info *mtd,
+                                   uint8_t *buf, int len);
        void            *priv;
 };
 
index 9aa2a9149b583991c75054ffd15b583b8e540f3e..8ed8733743818f17411b98aade04b9e4c63acef6 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/mtd/onenand_regs.h>
 #include <linux/mtd/bbm.h>
 
+#define MAX_DIES               2
 #define MAX_BUFFERRAM          2
 
 /* Scan and identify a OneNAND device */
@@ -51,7 +52,12 @@ struct onenand_bufferram {
 /**
  * struct onenand_chip - OneNAND Private Flash Chip Data
  * @base:              [BOARDSPECIFIC] address to access OneNAND
+ * @dies:              [INTERN][FLEX-ONENAND] number of dies on chip
+ * @boundary:          [INTERN][FLEX-ONENAND] Boundary of the dies
+ * @diesize:           [INTERN][FLEX-ONENAND] Size of the dies
  * @chipsize:          [INTERN] the size of one chip for multichip arrays
+ *                     FIXME For Flex-OneNAND, chipsize holds maximum possible
+ *                     device size ie when all blocks are considered MLC
  * @device_id:         [INTERN] device ID
  * @density_mask:      chip density, used for DDP devices
  * @verstion_id:       [INTERN] version ID
@@ -68,6 +74,8 @@ struct onenand_bufferram {
  * @command:           [REPLACEABLE] hardware specific function for writing
  *                     commands to the chip
  * @wait:              [REPLACEABLE] hardware specific function for wait on ready
+ * @bbt_wait:          [REPLACEABLE] hardware specific function for bbt wait on ready
+ * @unlock_all:                [REPLACEABLE] hardware specific function for unlock all
  * @read_bufferram:    [REPLACEABLE] hardware specific function for BufferRAM Area
  * @write_bufferram:   [REPLACEABLE] hardware specific function for BufferRAM Area
  * @read_word:         [REPLACEABLE] hardware specific function for read
@@ -92,9 +100,13 @@ struct onenand_bufferram {
  */
 struct onenand_chip {
        void __iomem            *base;
+       unsigned                dies;
+       unsigned                boundary[MAX_DIES];
+       loff_t                  diesize[MAX_DIES];
        unsigned int            chipsize;
        unsigned int            device_id;
        unsigned int            version_id;
+       unsigned int            technology;
        unsigned int            density_mask;
        unsigned int            options;
 
@@ -108,6 +120,8 @@ struct onenand_chip {
 
        int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len);
        int (*wait)(struct mtd_info *mtd, int state);
+       int (*bbt_wait)(struct mtd_info *mtd, int state);
+       void (*unlock_all)(struct mtd_info *mtd);
        int (*read_bufferram)(struct mtd_info *mtd, int area,
                        unsigned char *buffer, int offset, size_t count);
        int (*write_bufferram)(struct mtd_info *mtd, int area,
@@ -145,6 +159,8 @@ struct onenand_chip {
 #define ONENAND_SET_BUFFERRAM0(this)           (this->bufferram_index = 0)
 #define ONENAND_SET_BUFFERRAM1(this)           (this->bufferram_index = 1)
 
+#define FLEXONENAND(this)                                              \
+       (this->device_id & DEVICE_IS_FLEXONENAND)
 #define ONENAND_GET_SYS_CFG1(this)                                     \
        (this->read_word(this->base + ONENAND_REG_SYS_CFG1))
 #define ONENAND_SET_SYS_CFG1(v, this)                                  \
@@ -153,6 +169,9 @@ struct onenand_chip {
 #define ONENAND_IS_DDP(this)                                           \
        (this->device_id & ONENAND_DEVICE_IS_DDP)
 
+#define ONENAND_IS_MLC(this)                                           \
+       (this->technology & ONENAND_TECHNOLOGY_IS_MLC)
+
 #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM
 #define ONENAND_IS_2PLANE(this)                                                \
        (this->options & ONENAND_HAS_2PLANE)
@@ -169,6 +188,7 @@ struct onenand_chip {
 #define ONENAND_HAS_CONT_LOCK          (0x0001)
 #define ONENAND_HAS_UNLOCK_ALL         (0x0002)
 #define ONENAND_HAS_2PLANE             (0x0004)
+#define ONENAND_SKIP_UNLOCK_CHECK      (0x0100)
 #define ONENAND_PAGEBUF_ALLOC          (0x1000)
 #define ONENAND_OOBBUF_ALLOC           (0x2000)
 
@@ -176,6 +196,7 @@ struct onenand_chip {
  * OneNAND Flash Manufacturer ID Codes
  */
 #define ONENAND_MFR_SAMSUNG    0xec
+#define ONENAND_MFR_NUMONYX    0x20
 
 /**
  * struct onenand_manufacturers - NAND Flash Manufacturer ID Structure
@@ -189,5 +210,8 @@ struct onenand_manufacturers {
 
 int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
                         struct mtd_oob_ops *ops);
+unsigned onenand_block(struct onenand_chip *this, loff_t addr);
+loff_t onenand_addr(struct onenand_chip *this, int block);
+int flexonenand_region(struct mtd_info *mtd, loff_t addr);
 
 #endif /* __LINUX_MTD_ONENAND_H */
index 0c6bbe28f38ce91e01151faa517c75688300488b..86a6bbef646596c1735e509fece60d863b26d2e8 100644 (file)
@@ -67,6 +67,9 @@
 /*
  * Device ID Register F001h (R)
  */
+#define DEVICE_IS_FLEXONENAND          (1 << 9)
+#define FLEXONENAND_PI_MASK            (0x3ff)
+#define FLEXONENAND_PI_UNLOCK_SHIFT    (14)
 #define ONENAND_DEVICE_DENSITY_MASK    (0xf)
 #define ONENAND_DEVICE_DENSITY_SHIFT   (4)
 #define ONENAND_DEVICE_IS_DDP          (1 << 3)
  */
 #define ONENAND_VERSION_PROCESS_SHIFT  (8)
 
+/*
+ * Technology Register F006h (R)
+ */
+#define ONENAND_TECHNOLOGY_IS_MLC      (1 << 0)
+
 /*
  * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W)
  */
 /*
  * Start Address 8 F107h (R/W)
  */
-#define ONENAND_FPA_MASK               (0x3f)
+/* Note: It's actually 0x3f in case of SLC */
+#define ONENAND_FPA_MASK               (0x7f)
 #define ONENAND_FPA_SHIFT              (2)
 #define ONENAND_FSA_MASK               (0x03)
 
 #define ONENAND_BSA_BOOTRAM            (0 << 2)
 #define ONENAND_BSA_DATARAM0           (2 << 2)
 #define ONENAND_BSA_DATARAM1           (3 << 2)
-#define ONENAND_BSC_MASK               (0x03)
+/* Note: It's actually 0x03 in case of SLC */
+#define ONENAND_BSC_MASK               (0x07)
 
 /*
  * Command Register F220h (R/W)
 #define ONENAND_CMD_RESET              (0xF0)
 #define ONENAND_CMD_OTP_ACCESS         (0x65)
 #define ONENAND_CMD_READID             (0x90)
+#define FLEXONENAND_CMD_PI_UPDATE      (0x05)
+#define FLEXONENAND_CMD_PI_ACCESS      (0x66)
+#define FLEXONENAND_CMD_RECOVER_LSB    (0x05)
 
 /* NOTE: Those are not *REAL* commands */
 #define ONENAND_CMD_BUFFERRAM          (0x1978)
+#define FLEXONENAND_CMD_READ_PI                (0x1985)
 
 /*
  * System Configuration 1 Register F221h (R, R/W)
 #define ONENAND_ECC_1BIT_ALL           (0x5555)
 #define ONENAND_ECC_2BIT               (1 << 1)
 #define ONENAND_ECC_2BIT_ALL           (0xAAAA)
+#define FLEXONENAND_UNCORRECTABLE_ERROR        (0x1010)
 
 /*
  * One-Time Programmable (OTP)
  */
+#define FLEXONENAND_OTP_LOCK_OFFSET            (2048)
 #define ONENAND_OTP_LOCK_OFFSET                (14)
 
 #endif /* __ONENAND_REG_H */
index 7535a74083b92bc443231a728bd062497603c576..af6dcb992bc35896a131d58c4871570436c5b795 100644 (file)
@@ -40,7 +40,6 @@ struct mtd_partition {
        uint64_t offset;                /* offset within the master MTD space */
        uint32_t mask_flags;            /* master MTD flags to mask out for this partition */
        struct nand_ecclayout *ecclayout;       /* out of band layout for this partition (NAND only)*/
-       struct mtd_info **mtdp;         /* pointer to store the MTD object */
 };
 
 #define MTDPART_OFS_NXTBLK     (-2)
index 8eb018f96002d84f7ae3a0810023cffc65333f04..192f8fb7d5463318c5b8d5e78c8d60738983d26c 100644 (file)
@@ -1,5 +1,4 @@
 header-y += inftl-user.h
-header-y += jffs2-user.h
 header-y += mtd-abi.h
 header-y += mtd-user.h
 header-y += nftl-user.h
diff --git a/include/mtd/jffs2-user.h b/include/mtd/jffs2-user.h
deleted file mode 100644 (file)
index fa94b0e..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * JFFS2 definitions for use in user space only
- */
-
-#ifndef __JFFS2_USER_H__
-#define __JFFS2_USER_H__
-
-/* This file is blessed for inclusion by userspace */
-#include <linux/jffs2.h>
-#include <linux/types.h>
-#include <endian.h>
-#include <byteswap.h>
-
-#undef cpu_to_je16
-#undef cpu_to_je32
-#undef cpu_to_jemode
-#undef je16_to_cpu
-#undef je32_to_cpu
-#undef jemode_to_cpu
-
-extern int target_endian;
-
-#define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
-#define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
-
-#define cpu_to_je16(x) ((jint16_t){t16(x)})
-#define cpu_to_je32(x) ((jint32_t){t32(x)})
-#define cpu_to_jemode(x) ((jmode_t){t32(x)})
-
-#define je16_to_cpu(x) (t16((x).v16))
-#define je32_to_cpu(x) (t32((x).v32))
-#define jemode_to_cpu(x) (t32((x).m))
-
-#endif /* __JFFS2_USER_H__ */
index b6595b3c68b6f9117dbb582dab66e53d9daf7609..be51ae2bd0ffd955428eaabf00317ba45d28a569 100644 (file)
@@ -12,12 +12,24 @@ struct erase_info_user {
        __u32 length;
 };
 
+struct erase_info_user64 {
+       __u64 start;
+       __u64 length;
+};
+
 struct mtd_oob_buf {
        __u32 start;
        __u32 length;
        unsigned char __user *ptr;
 };
 
+struct mtd_oob_buf64 {
+       __u64 start;
+       __u32 pad;
+       __u32 length;
+       __u64 usr_ptr;
+};
+
 #define MTD_ABSENT             0
 #define MTD_RAM                        1
 #define MTD_ROM                        2
@@ -95,6 +107,9 @@ struct otp_info {
 #define ECCGETLAYOUT           _IOR('M', 17, struct nand_ecclayout)
 #define ECCGETSTATS            _IOR('M', 18, struct mtd_ecc_stats)
 #define MTDFILEMODE            _IO('M', 19)
+#define MEMERASE64             _IOW('M', 20, struct erase_info_user64)
+#define MEMWRITEOOB64          _IOWR('M', 21, struct mtd_oob_buf64)
+#define MEMREADOOB64           _IOWR('M', 22, struct mtd_oob_buf64)
 
 /*
  * Obsolete legacy interface. Keep it in order not to break userspace