Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux...
authorDavid Woodhouse <David.Woodhouse@intel.com>
Sat, 30 Oct 2010 11:35:11 +0000 (12:35 +0100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Sat, 30 Oct 2010 11:35:11 +0000 (12:35 +0100)
Conflicts:
drivers/mtd/mtd_blkdevs.c

Merge Grant's device-tree bits so that we can apply the subsequent fixes.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
71 files changed:
arch/arm/mach-u300/clock.c
arch/arm/mach-u300/core.c
arch/arm/mach-u300/include/mach/u300-regs.h
arch/arm/plat-pxa/include/plat/pxa3xx_nand.h
arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h [new file with mode: 0644]
arch/powerpc/Kconfig
arch/powerpc/include/asm/fsl_lbc.h
arch/powerpc/sysdev/fsl_lbc.c
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/cfi_probe.c
drivers/mtd/chips/cfi_util.c
drivers/mtd/devices/block2mtd.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/phram.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/bcm963xx-flash.c [new file with mode: 0644]
drivers/mtd/maps/gpio-addr-flash.c
drivers/mtd/maps/pcmciamtd.c
drivers/mtd/maps/physmap_of.c
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/bf5xx_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/denali.c
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/fsl_upm.c
drivers/mtd/nand/fsmc_nand.c [new file with mode: 0644]
drivers/mtd/nand/mpc5121_nfc.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_bbt.c
drivers/mtd/nand/nand_ids.c
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/ndfc.c
drivers/mtd/nand/omap2.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/mtd/nand/r852.c
drivers/mtd/nand/r852.h
drivers/mtd/ofpart.c
drivers/mtd/onenand/Kconfig
drivers/mtd/onenand/onenand_base.c
drivers/mtd/onenand/samsung.c
drivers/mtd/sm_ftl.h
fs/jffs2/build.c
fs/jffs2/compr.c
fs/jffs2/compr.h
fs/jffs2/compr_lzo.c
fs/jffs2/compr_rtime.c
fs/jffs2/compr_rubin.c
fs/jffs2/compr_zlib.c
fs/jffs2/dir.c
fs/jffs2/erase.c
fs/jffs2/fs.c
fs/jffs2/gc.c
fs/jffs2/jffs2_fs_sb.h
fs/jffs2/nodelist.c
fs/jffs2/nodelist.h
fs/jffs2/scan.c
include/linux/mtd/bbm.h
include/linux/mtd/cfi.h
include/linux/mtd/fsmc.h [new file with mode: 0644]
include/linux/mtd/inftl.h
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
include/linux/mtd/partitions.h
include/mtd/mtd-abi.h
include/mtd/mtd-user.h

index 60acf9e..7458fc6 100644 (file)
@@ -66,7 +66,7 @@ static DEFINE_SPINLOCK(syscon_resetreg_lock);
  *  AMBA bus
  *  |
  *  +- CPU
- *  +- NANDIF NAND Flash interface
+ *  +- FSMC NANDIF NAND Flash interface
  *  +- SEMI Shared Memory interface
  *  +- ISP Image Signal Processor (U335 only)
  *  +- CDS (U335 only)
@@ -726,7 +726,7 @@ static struct clk cpu_clk = {
 };
 
 static struct clk nandif_clk = {
-       .name       = "NANDIF",
+       .name       = "FSMC",
        .parent     = &amba_clk,
        .hw_ctrld   = false,
        .reset      = true,
@@ -1259,7 +1259,7 @@ static struct clk_lookup lookups[] = {
        /* Connected directly to the AMBA bus */
        DEF_LOOKUP("amba",      &amba_clk),
        DEF_LOOKUP("cpu",       &cpu_clk),
-       DEF_LOOKUP("fsmc",      &nandif_clk),
+       DEF_LOOKUP("fsmc-nand", &nandif_clk),
        DEF_LOOKUP("semi",      &semi_clk),
 #ifdef CONFIG_MACH_U300_BS335
        DEF_LOOKUP("isp",       &isp_clk),
index ea41c23..aa53ee2 100644 (file)
@@ -21,7 +21,8 @@
 #include <linux/gpio.h>
 #include <linux/clk.h>
 #include <linux/err.h>
-#include <mach/coh901318.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/fsmc.h>
 
 #include <asm/types.h>
 #include <asm/setup.h>
@@ -30,6 +31,7 @@
 #include <asm/mach/map.h>
 #include <asm/mach/irq.h>
 
+#include <mach/coh901318.h>
 #include <mach/hardware.h>
 #include <mach/syscon.h>
 #include <mach/dma_channels.h>
@@ -285,6 +287,13 @@ static struct resource rtc_resources[] = {
  */
 static struct resource fsmc_resources[] = {
        {
+               .name  = "nand_data",
+               .start = U300_NAND_CS0_PHYS_BASE,
+               .end   = U300_NAND_CS0_PHYS_BASE + SZ_16K - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .name  = "fsmc_regs",
                .start = U300_NAND_IF_PHYS_BASE,
                .end   = U300_NAND_IF_PHYS_BASE + SZ_4K - 1,
                .flags = IORESOURCE_MEM,
@@ -1429,11 +1438,39 @@ static struct platform_device rtc_device = {
        .resource = rtc_resources,
 };
 
-static struct platform_device fsmc_device = {
-       .name = "nandif",
+static struct mtd_partition u300_partitions[] = {
+       {
+               .name = "bootrecords",
+               .offset = 0,
+               .size = SZ_128K,
+       },
+       {
+               .name = "free",
+               .offset = SZ_128K,
+               .size = 8064 * SZ_1K,
+       },
+       {
+               .name = "platform",
+               .offset = 8192 * SZ_1K,
+               .size = 253952 * SZ_1K,
+       },
+};
+
+static struct fsmc_nand_platform_data nand_platform_data = {
+       .partitions = u300_partitions,
+       .nr_partitions = ARRAY_SIZE(u300_partitions),
+       .options = NAND_SKIP_BBTSCAN,
+       .width = FSMC_NAND_BW8,
+};
+
+static struct platform_device nand_device = {
+       .name = "fsmc-nand",
        .id = -1,
-       .num_resources = ARRAY_SIZE(fsmc_resources),
        .resource = fsmc_resources,
+       .num_resources = ARRAY_SIZE(fsmc_resources),
+       .dev = {
+               .platform_data = &nand_platform_data,
+       },
 };
 
 static struct platform_device ave_device = {
@@ -1465,7 +1502,7 @@ static struct platform_device *platform_devs[] __initdata = {
        &keypad_device,
        &rtc_device,
        &gpio_device,
-       &fsmc_device,
+       &nand_device,
        &wdog_device,
        &ave_device
 };
index 56721a0..8b85df4 100644 (file)
 
 /* NAND Flash CS0 */
 #define U300_NAND_CS0_PHYS_BASE                0x80000000
-#define U300_NAND_CS0_VIRT_BASE                0xff040000
 
 /* NFIF */
 #define U300_NAND_IF_PHYS_BASE         0x9f800000
-#define U300_NAND_IF_VIRT_BASE         0xff030000
 
 /* AHB Peripherals */
 #define U300_AHB_PER_PHYS_BASE         0xa0000000
index 3478eae..01a8448 100644 (file)
@@ -30,15 +30,15 @@ struct pxa3xx_nand_cmdset {
 };
 
 struct pxa3xx_nand_flash {
-       const struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
-       const struct pxa3xx_nand_cmdset *cmdset;
-
-       uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */
-       uint32_t page_size;     /* Page size in bytes (PAGE_SZ) */
-       uint32_t flash_width;   /* Width of Flash memory (DWIDTH_M) */
-       uint32_t dfc_width;     /* Width of flash controller(DWIDTH_C) */
-       uint32_t num_blocks;    /* Number of physical blocks in Flash */
-       uint32_t chip_id;
+       uint32_t        chip_id;
+       unsigned int    page_per_block; /* Pages per block (PG_PER_BLK) */
+       unsigned int    page_size;      /* Page size in bytes (PAGE_SZ) */
+       unsigned int    flash_width;    /* Width of Flash memory (DWIDTH_M) */
+       unsigned int    dfc_width;      /* Width of flash controller(DWIDTH_C) */
+       unsigned int    num_blocks;     /* Number of physical blocks in Flash */
+
+       struct pxa3xx_nand_cmdset *cmdset;      /* NAND command set */
+       struct pxa3xx_nand_timing *timing;      /* NAND Flash timing */
 };
 
 struct pxa3xx_nand_platform_data {
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h b/arch/mips/include/asm/mach-bcm63xx/bcm963xx_tag.h
new file mode 100644 (file)
index 0000000..5325084
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef __BCM963XX_TAG_H
+#define __BCM963XX_TAG_H
+
+#define TAGVER_LEN             4       /* Length of Tag Version */
+#define TAGLAYOUT_LEN          4       /* Length of FlashLayoutVer */
+#define SIG1_LEN               20      /* Company Signature 1 Length */
+#define SIG2_LEN               14      /* Company Signature 2 Lenght */
+#define BOARDID_LEN            16      /* Length of BoardId */
+#define ENDIANFLAG_LEN         2       /* Endian Flag Length */
+#define CHIPID_LEN             6       /* Chip Id Length */
+#define IMAGE_LEN              10      /* Length of Length Field */
+#define ADDRESS_LEN            12      /* Length of Address field */
+#define DUALFLAG_LEN           2       /* Dual Image flag Length */
+#define INACTIVEFLAG_LEN       2       /* Inactie Flag Length */
+#define RSASIG_LEN             20      /* Length of RSA Signature in tag */
+#define TAGINFO1_LEN           30      /* Length of vendor information field1 in tag */
+#define FLASHLAYOUTVER_LEN     4       /* Length of Flash Layout Version String tag */
+#define TAGINFO2_LEN           16      /* Length of vendor information field2 in tag */
+#define CRC_LEN                        4       /* Length of CRC in bytes */
+#define ALTTAGINFO_LEN         54      /* Alternate length for vendor information; Pirelli */
+
+#define NUM_PIRELLI            2
+#define IMAGETAG_CRC_START     0xFFFFFFFF
+
+#define PIRELLI_BOARDS { \
+       "AGPF-S0", \
+       "DWV-S0", \
+}
+
+/*
+ * The broadcom firmware assumes the rootfs starts the image,
+ * therefore uses the rootfs start (flash_image_address)
+ * to determine where to flash the image.  Since we have the kernel first
+ * we have to give it the kernel address, but the crc uses the length
+ * associated with this address (root_length), which is added to the kernel
+ * length (kernel_length) to determine the length of image to flash and thus
+ * needs to be rootfs + deadcode (jffs2 EOF marker)
+*/
+
+struct bcm_tag {
+       /* 0-3: Version of the image tag */
+       char tag_version[TAGVER_LEN];
+       /* 4-23: Company Line 1 */
+       char sig_1[SIG1_LEN];
+       /*  24-37: Company Line 2 */
+       char sig_2[SIG2_LEN];
+       /* 38-43: Chip this image is for */
+       char chip_id[CHIPID_LEN];
+       /* 44-59: Board name */
+       char board_id[BOARDID_LEN];
+       /* 60-61: Map endianness -- 1 BE 0 LE */
+       char big_endian[ENDIANFLAG_LEN];
+       /* 62-71: Total length of image */
+       char total_length[IMAGE_LEN];
+       /* 72-83: Address in memory of CFE */
+       char cfe__address[ADDRESS_LEN];
+       /* 84-93: Size of CFE */
+       char cfe_length[IMAGE_LEN];
+       /* 94-105: Address in memory of image start
+        * (kernel for OpenWRT, rootfs for stock firmware)
+        */
+       char flash_image_start[ADDRESS_LEN];
+       /* 106-115: Size of rootfs */
+       char root_length[IMAGE_LEN];
+       /* 116-127: Address in memory of kernel */
+       char kernel_address[ADDRESS_LEN];
+       /* 128-137: Size of kernel */
+       char kernel_length[IMAGE_LEN];
+       /* 138-139: Unused at the moment */
+       char dual_image[DUALFLAG_LEN];
+       /* 140-141: Unused at the moment */
+       char inactive_flag[INACTIVEFLAG_LEN];
+       /* 142-161: RSA Signature (not used; some vendors may use this) */
+       char rsa_signature[RSASIG_LEN];
+       /* 162-191: Compilation and related information (not used in OpenWrt) */
+       char information1[TAGINFO1_LEN];
+       /* 192-195: Version flash layout */
+       char flash_layout_ver[FLASHLAYOUTVER_LEN];
+       /* 196-199: kernel+rootfs CRC32 */
+       char fskernel_crc[CRC_LEN];
+       /* 200-215: Unused except on Alice Gate where is is information */
+       char information2[TAGINFO2_LEN];
+       /* 216-219: CRC32 of image less imagetag (kernel for Alice Gate) */
+       char image_crc[CRC_LEN];
+       /* 220-223: CRC32 of rootfs partition */
+       char rootfs_crc[CRC_LEN];
+       /* 224-227: CRC32 of kernel partition */
+       char kernel_crc[CRC_LEN];
+       /* 228-235: Unused at present */
+       char reserved1[8];
+       /* 236-239: CRC32 of header excluding tagVersion */
+       char header_crc[CRC_LEN];
+       /* 240-255: Unused at present */
+       char reserved2[16];
+};
+
+#endif /* __BCM63XX_TAG_H */
index 4b1e521..2915891 100644 (file)
@@ -688,9 +688,12 @@ config 4xx_SOC
        bool
 
 config FSL_LBC
-       bool
+       bool "Freescale Local Bus support"
+       depends on FSL_SOC
        help
-         Freescale Localbus support
+         Enables reporting of errors from the Freescale local bus
+         controller.  Also contains some common code used by
+         drivers for specific local bus peripherals.
 
 config FSL_GTM
        bool
index 1b5a210..5c1bf34 100644 (file)
@@ -1,9 +1,10 @@
 /* Freescale Local Bus Controller
  *
- * Copyright (c) 2006-2007 Freescale Semiconductor
+ * Copyright Â© 2006-2007, 2010 Freescale Semiconductor
  *
  * Authors: Nick Spence <nick.spence@freescale.com>,
  *          Scott Wood <scottwood@freescale.com>
+ *          Jack Lan <jack.lan@freescale.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
@@ -26,6 +27,8 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/io.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
 
 struct fsl_lbc_bank {
        __be32 br;             /**< Base Register  */
@@ -125,13 +128,23 @@ struct fsl_lbc_regs {
 #define LTESR_ATMW 0x00800000
 #define LTESR_ATMR 0x00400000
 #define LTESR_CS   0x00080000
+#define LTESR_UPM  0x00000002
 #define LTESR_CC   0x00000001
 #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
+#define LTESR_MASK      (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
+                        | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
+                        | LTESR_CC)
+#define LTESR_CLEAR    0xFFFFFFFF
+#define LTECCR_CLEAR   0xFFFFFFFF
+#define LTESR_STATUS   LTESR_MASK
+#define LTEIR_ENABLE   LTESR_MASK
+#define LTEDR_ENABLE   0x00000000
        __be32 ltedr;           /**< Transfer Error Disable Register */
        __be32 lteir;           /**< Transfer Error Interrupt Register */
        __be32 lteatr;          /**< Transfer Error Attributes Register */
        __be32 ltear;           /**< Transfer Error Address Register */
-       u8 res6[0xC];
+       __be32 lteccr;          /**< Transfer Error ECC Register */
+       u8 res6[0x8];
        __be32 lbcr;            /**< Configuration Register */
 #define LBCR_LDIS  0x80000000
 #define LBCR_LDIS_SHIFT    31
@@ -235,6 +248,7 @@ struct fsl_upm {
        int width;
 };
 
+extern u32 fsl_lbc_addr(phys_addr_t addr_base);
 extern int fsl_lbc_find(phys_addr_t addr_base);
 extern int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm);
 
@@ -265,7 +279,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm *upm)
                cpu_relax();
 }
 
+/* overview of the fsl lbc controller */
+
+struct fsl_lbc_ctrl {
+       /* device info */
+       struct device                   *dev;
+       struct fsl_lbc_regs __iomem     *regs;
+       int                             irq;
+       wait_queue_head_t               irq_wait;
+       spinlock_t                      lock;
+       void                            *nand;
+
+       /* status read from LTESR by irq handler */
+       unsigned int                    irq_status;
+};
+
 extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
                               u32 mar);
+extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
 
 #endif /* __ASM_FSL_LBC_H */
index dceb8d1..4fcb5a4 100644 (file)
@@ -1,9 +1,12 @@
 /*
  * Freescale LBC and UPM routines.
  *
- * Copyright (c) 2007-2008  MontaVista Software, Inc.
+ * Copyright Â© 2007-2008  MontaVista Software, Inc.
+ * Copyright Â© 2010 Freescale Semiconductor
  *
  * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ * Author: Jack Lan <Jack.Lan@freescale.com>
+ * Author: Roy Zang <tie-fei.zang@freescale.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
 #include <linux/types.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
 #include <asm/prom.h>
 #include <asm/fsl_lbc.h>
 
 static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
-static struct fsl_lbc_regs __iomem *fsl_lbc_regs;
+struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
+EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
 
-static char __initdata *compat_lbc[] = {
-       "fsl,pq2-localbus",
-       "fsl,pq2pro-localbus",
-       "fsl,pq3-localbus",
-       "fsl,elbc",
-};
-
-static int __init fsl_lbc_init(void)
+/**
+ * fsl_lbc_addr - convert the base address
+ * @addr_base: base address of the memory bank
+ *
+ * This function converts a base address of lbc into the right format for the
+ * BR register. If the SOC has eLBC then it returns 32bit physical address
+ * else it convers a 34bit local bus physical address to correct format of
+ * 32bit address for BR register (Example: MPC8641).
+ */
+u32 fsl_lbc_addr(phys_addr_t addr_base)
 {
-       struct device_node *lbus;
-       int i;
+       struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node;
+       u32 addr = addr_base & 0xffff8000;
 
-       for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) {
-               lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]);
-               if (lbus)
-                       goto found;
-       }
-       return -ENODEV;
+       if (of_device_is_compatible(np, "fsl,elbc"))
+               return addr;
 
-found:
-       fsl_lbc_regs = of_iomap(lbus, 0);
-       of_node_put(lbus);
-       if (!fsl_lbc_regs)
-               return -ENOMEM;
-       return 0;
+       return addr | ((addr_base & 0x300000000ull) >> 19);
 }
-arch_initcall(fsl_lbc_init);
+EXPORT_SYMBOL(fsl_lbc_addr);
 
 /**
  * fsl_lbc_find - find Localbus bank
@@ -65,15 +66,17 @@ arch_initcall(fsl_lbc_init);
 int fsl_lbc_find(phys_addr_t addr_base)
 {
        int i;
+       struct fsl_lbc_regs __iomem *lbc;
 
-       if (!fsl_lbc_regs)
+       if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
                return -ENODEV;
 
-       for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) {
-               __be32 br = in_be32(&fsl_lbc_regs->bank[i].br);
-               __be32 or = in_be32(&fsl_lbc_regs->bank[i].or);
+       lbc = fsl_lbc_ctrl_dev->regs;
+       for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
+               __be32 br = in_be32(&lbc->bank[i].br);
+               __be32 or = in_be32(&lbc->bank[i].or);
 
-               if (br & BR_V && (br & or & BR_BA) == addr_base)
+               if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base))
                        return i;
        }
 
@@ -94,22 +97,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
 {
        int bank;
        __be32 br;
+       struct fsl_lbc_regs __iomem *lbc;
 
        bank = fsl_lbc_find(addr_base);
        if (bank < 0)
                return bank;
 
-       br = in_be32(&fsl_lbc_regs->bank[bank].br);
+       if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+               return -ENODEV;
+
+       lbc = fsl_lbc_ctrl_dev->regs;
+       br = in_be32(&lbc->bank[bank].br);
 
        switch (br & BR_MSEL) {
        case BR_MS_UPMA:
-               upm->mxmr = &fsl_lbc_regs->mamr;
+               upm->mxmr = &lbc->mamr;
                break;
        case BR_MS_UPMB:
-               upm->mxmr = &fsl_lbc_regs->mbmr;
+               upm->mxmr = &lbc->mbmr;
                break;
        case BR_MS_UPMC:
-               upm->mxmr = &fsl_lbc_regs->mcmr;
+               upm->mxmr = &lbc->mcmr;
                break;
        default:
                return -EINVAL;
@@ -148,9 +156,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
        int ret = 0;
        unsigned long flags;
 
+       if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+               return -ENODEV;
+
        spin_lock_irqsave(&fsl_lbc_lock, flags);
 
-       out_be32(&fsl_lbc_regs->mar, mar);
+       out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
 
        switch (upm->width) {
        case 8:
@@ -172,3 +183,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
        return ret;
 }
 EXPORT_SYMBOL(fsl_upm_run_pattern);
+
+static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
+{
+       struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+
+       /* clear event registers */
+       setbits32(&lbc->ltesr, LTESR_CLEAR);
+       out_be32(&lbc->lteatr, 0);
+       out_be32(&lbc->ltear, 0);
+       out_be32(&lbc->lteccr, LTECCR_CLEAR);
+       out_be32(&lbc->ltedr, LTEDR_ENABLE);
+
+       /* Enable interrupts for any detected events */
+       out_be32(&lbc->lteir, LTEIR_ENABLE);
+
+       return 0;
+}
+
+/*
+ * NOTE: This interrupt is used to report localbus events of various kinds,
+ * such as transaction errors on the chipselects.
+ */
+
+static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
+{
+       struct fsl_lbc_ctrl *ctrl = data;
+       struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+       u32 status;
+
+       status = in_be32(&lbc->ltesr);
+       if (!status)
+               return IRQ_NONE;
+
+       out_be32(&lbc->ltesr, LTESR_CLEAR);
+       out_be32(&lbc->lteatr, 0);
+       out_be32(&lbc->ltear, 0);
+       ctrl->irq_status = status;
+
+       if (status & LTESR_BM)
+               dev_err(ctrl->dev, "Local bus monitor time-out: "
+                       "LTESR 0x%08X\n", status);
+       if (status & LTESR_WP)
+               dev_err(ctrl->dev, "Write protect error: "
+                       "LTESR 0x%08X\n", status);
+       if (status & LTESR_ATMW)
+               dev_err(ctrl->dev, "Atomic write error: "
+                       "LTESR 0x%08X\n", status);
+       if (status & LTESR_ATMR)
+               dev_err(ctrl->dev, "Atomic read error: "
+                       "LTESR 0x%08X\n", status);
+       if (status & LTESR_CS)
+               dev_err(ctrl->dev, "Chip select error: "
+                       "LTESR 0x%08X\n", status);
+       if (status & LTESR_UPM)
+               ;
+       if (status & LTESR_FCT) {
+               dev_err(ctrl->dev, "FCM command time-out: "
+                       "LTESR 0x%08X\n", status);
+               smp_wmb();
+               wake_up(&ctrl->irq_wait);
+       }
+       if (status & LTESR_PAR) {
+               dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
+                       "LTESR 0x%08X\n", status);
+               smp_wmb();
+               wake_up(&ctrl->irq_wait);
+       }
+       if (status & LTESR_CC) {
+               smp_wmb();
+               wake_up(&ctrl->irq_wait);
+       }
+       if (status & ~LTESR_MASK)
+               dev_err(ctrl->dev, "Unknown error: "
+                       "LTESR 0x%08X\n", status);
+       return IRQ_HANDLED;
+}
+
+/*
+ * fsl_lbc_ctrl_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code allocates all of
+ * the resources needed for the controller only.  The
+ * resources for the NAND banks themselves are allocated
+ * in the chip probe function.
+*/
+
+static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
+{
+       int ret;
+
+       if (!dev->dev.of_node) {
+               dev_err(&dev->dev, "Device OF-Node is NULL");
+               return -EFAULT;
+       }
+
+       fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
+       if (!fsl_lbc_ctrl_dev)
+               return -ENOMEM;
+
+       dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
+
+       spin_lock_init(&fsl_lbc_ctrl_dev->lock);
+       init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
+
+       fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
+       if (!fsl_lbc_ctrl_dev->regs) {
+               dev_err(&dev->dev, "failed to get memory region\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
+       if (fsl_lbc_ctrl_dev->irq == NO_IRQ) {
+               dev_err(&dev->dev, "failed to get irq resource\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       fsl_lbc_ctrl_dev->dev = &dev->dev;
+
+       ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev);
+       if (ret < 0)
+               goto err;
+
+       ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0,
+                               "fsl-lbc", fsl_lbc_ctrl_dev);
+       if (ret != 0) {
+               dev_err(&dev->dev, "failed to install irq (%d)\n",
+                       fsl_lbc_ctrl_dev->irq);
+               ret = fsl_lbc_ctrl_dev->irq;
+               goto err;
+       }
+
+       return 0;
+
+err:
+       iounmap(fsl_lbc_ctrl_dev->regs);
+       kfree(fsl_lbc_ctrl_dev);
+       return ret;
+}
+
+static const struct of_device_id fsl_lbc_match[] = {
+       { .compatible = "fsl,elbc", },
+       { .compatible = "fsl,pq3-localbus", },
+       { .compatible = "fsl,pq2-localbus", },
+       { .compatible = "fsl,pq2pro-localbus", },
+       {},
+};
+
+static struct platform_driver fsl_lbc_ctrl_driver = {
+       .driver = {
+               .name = "fsl-lbc",
+               .of_match_table = fsl_lbc_match,
+       },
+       .probe = fsl_lbc_ctrl_probe,
+};
+
+static int __init fsl_lbc_init(void)
+{
+       return platform_driver_register(&fsl_lbc_ctrl_driver);
+}
+module_init(fsl_lbc_init);
index 9e2b7e9..ad9268b 100644 (file)
@@ -1496,7 +1496,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
 
        switch (mode) {
        case FL_WRITING:
-               write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41);
+               write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0x40) : CMD(0x41);
                break;
        case FL_OTP_WRITE:
                write_cmd = CMD(0xc0);
@@ -1661,7 +1661,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
        cmd_adr = adr & ~(wbufsize-1);
 
        /* Let's determine this according to the interleave only once */
-       write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9);
+       write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9);
 
        mutex_lock(&chip->mutex);
        ret = get_chip(map, chip, cmd_adr, FL_WRITING);
index ba29d2f..3b8e32d 100644 (file)
@@ -291,6 +291,23 @@ static void fixup_sst39vf_rev_b(struct mtd_info *mtd, void *param)
 
        cfi->addr_unlock1 = 0x555;
        cfi->addr_unlock2 = 0x2AA;
+
+       cfi->sector_erase_cmd = CMD(0x50);
+}
+
+static void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd, void *param)
+{
+       struct map_info *map = mtd->priv;
+       struct cfi_private *cfi = map->fldrv_priv;
+
+       fixup_sst39vf_rev_b(mtd, param);
+
+       /*
+        * CFI reports 1024 sectors (0x03ff+1) of 64KBytes (0x0100*256) where
+        * it should report a size of 8KBytes (0x0020*256).
+        */
+       cfi->cfiq->EraseRegionInfo[0] = 0x002003ff;
+       pr_warning("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n", mtd->name);
 }
 
 static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param)
@@ -317,14 +334,14 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
 
 /* Used to fix CFI-Tables of chips without Extended Query Tables */
 static struct cfi_fixup cfi_nopri_fixup_table[] = {
-       { CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, // SST39VF1602
-       { CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, // SST39VF1601
-       { CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, // SST39VF3202
-       { CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, // SST39VF3201
-       { CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, // SST39VF3202B
-       { CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, // SST39VF3201B
-       { CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, // SST39VF6402B
-       { CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, // SST39VF6401B
+       { CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, /* SST39VF1602 */
+       { CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, /* SST39VF1601 */
+       { CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, /* SST39VF3202 */
+       { CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, /* SST39VF3201 */
+       { CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, /* SST39VF3202B */
+       { CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, /* SST39VF3201B */
+       { CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, /* SST39VF6402B */
+       { CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, /* SST39VF6401B */
        { 0, 0, NULL, NULL }
 };
 
@@ -344,6 +361,10 @@ static struct cfi_fixup cfi_fixup_table[] = {
        { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
        { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
        { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
+       { CFI_MFR_SST, 0x536A, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6402 */
+       { CFI_MFR_SST, 0x536B, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6401 */
+       { CFI_MFR_SST, 0x536C, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6404 */
+       { CFI_MFR_SST, 0x536D, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6403 */
 #if !FORCE_WORD_WRITE
        { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
 #endif
@@ -374,6 +395,13 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi,
        if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e &&
            extp->MajorVersion == '0')
                extp->MajorVersion = '1';
+       /*
+        * SST 38VF640x chips report major=0xFF / minor=0xFF.
+        */
+       if (cfi->mfr == CFI_MFR_SST && (cfi->id >> 4) == 0x0536) {
+               extp->MajorVersion = '1';
+               extp->MinorVersion = '0';
+       }
 }
 
 struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
@@ -545,15 +573,6 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
                printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
                goto setup_err;
        }
-#if 0
-       // debug
-       for (i=0; i<mtd->numeraseregions;i++){
-               printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
-                      i,mtd->eraseregions[i].offset,
-                      mtd->eraseregions[i].erasesize,
-                      mtd->eraseregions[i].numblocks);
-       }
-#endif
 
        __module_get(THIS_MODULE);
        register_reboot_notifier(&mtd->reboot_notifier);
@@ -674,7 +693,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
                                 * there was an error (so leave the erase
                                 * routine to recover from it) or we trying to
                                 * use the erase-in-progress sector. */
-                               map_write(map, CMD(0x30), chip->in_progress_block_addr);
+                               map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
                                chip->state = FL_ERASING;
                                chip->oldstate = FL_READY;
                                printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
@@ -727,7 +746,7 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
        switch(chip->oldstate) {
        case FL_ERASING:
                chip->state = chip->oldstate;
-               map_write(map, CMD(0x30), chip->in_progress_block_addr);
+               map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
                chip->oldstate = FL_READY;
                chip->state = FL_ERASING;
                break;
@@ -870,7 +889,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
                        local_irq_disable();
 
                        /* Resume the write or erase operation */
-                       map_write(map, CMD(0x30), adr);
+                       map_write(map, cfi->sector_erase_cmd, adr);
                        chip->state = oldstate;
                        start = xip_currtime();
                } else if (usec >= 1000000/HZ) {
@@ -1025,9 +1044,6 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
        mutex_lock(&chip->mutex);
 
        if (chip->state != FL_READY){
-#if 0
-               printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
-#endif
                set_current_state(TASK_UNINTERRUPTIBLE);
                add_wait_queue(&chip->wq, &wait);
 
@@ -1035,10 +1051,6 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
 
                schedule();
                remove_wait_queue(&chip->wq, &wait);
-#if 0
-               if(signal_pending(current))
-                       return -EINTR;
-#endif
                timeo = jiffies + HZ;
 
                goto retry;
@@ -1246,9 +1258,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                mutex_lock(&cfi->chips[chipnum].mutex);
 
                if (cfi->chips[chipnum].state != FL_READY) {
-#if 0
-                       printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
-#endif
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&cfi->chips[chipnum].wq, &wait);
 
@@ -1256,10 +1265,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
 
                        schedule();
                        remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
-#if 0
-                       if(signal_pending(current))
-                               return -EINTR;
-#endif
                        goto retry;
                }
 
@@ -1324,9 +1329,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                mutex_lock(&cfi->chips[chipnum].mutex);
 
                if (cfi->chips[chipnum].state != FL_READY) {
-#if 0
-                       printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
-#endif
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&cfi->chips[chipnum].wq, &wait);
 
@@ -1334,10 +1336,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
 
                        schedule();
                        remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
-#if 0
-                       if(signal_pending(current))
-                               return -EINTR;
-#endif
                        goto retry1;
                }
 
@@ -1396,7 +1394,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
 
        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
-       //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
 
        /* Write Buffer Load */
        map_write(map, CMD(0x25), cmd_adr);
@@ -1675,7 +1672,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
        cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
-       map_write(map, CMD(0x30), adr);
+       map_write(map, cfi->sector_erase_cmd, adr);
 
        chip->state = FL_ERASING;
        chip->erase_suspended = 0;
index 8f5b96a..d255352 100644 (file)
@@ -177,6 +177,8 @@ static int __xipram cfi_chip_setup(struct map_info *map,
 
        cfi->cfi_mode = CFI_MODE_CFI;
 
+       cfi->sector_erase_cmd = CMD(0x30);
+
        /* Read the CFI info structure */
        xip_disable_qry(base, map, cfi);
        for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++)
index e503b2c..360525c 100644 (file)
@@ -75,6 +75,13 @@ int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
        cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
+       if (cfi_qry_present(map, base, cfi))
+               return 1;
+       /* SST 39VF640xB */
+       cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0xAA, 0x555, base, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x55, 0x2AA, base, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
        if (cfi_qry_present(map, base, cfi))
                return 1;
        /* QRY not found */
index 9365186..2cf0cc6 100644 (file)
@@ -91,7 +91,6 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
        } else
                instr->state = MTD_ERASE_DONE;
 
-       instr->state = MTD_ERASE_DONE;
        mtd_erase_callback(instr);
        return err;
 }
index ea22520..669d2b7 100644 (file)
@@ -661,11 +661,14 @@ static const struct spi_device_id m25p_ids[] = {
        { "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) },
        { "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) },
        { "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) },
+       { "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, SECT_4K) },
        { "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) },
        { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
        { "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
        { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
        { "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
+       { "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) },
+       { "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) },
 
        /* SST -- large erase sizes are "overlays", "sectors" are 4K */
        { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K) },
@@ -714,6 +717,7 @@ static const struct spi_device_id m25p_ids[] = {
        { "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) },
        { "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) },
        { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
+       { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
 
        /* Catalyst / On Semiconductor -- non-JEDEC */
        { "cat25c11", CAT25_INFO(  16, 8, 16, 1) },
index 1696bbe..5239328 100644 (file)
@@ -15,7 +15,7 @@
  *     phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
  */
 
-#define pr_fmt(fmt) "phram: " fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <asm/io.h>
 #include <linux/init.h>
index 9622126..a0dd7bb 100644 (file)
@@ -251,6 +251,15 @@ config MTD_NETtel
        help
          Support for flash chips on NETtel/SecureEdge/SnapGear boards.
 
+config MTD_BCM963XX
+        tristate "Map driver for Broadcom BCM963xx boards"
+        depends on BCM63XX
+       select MTD_MAP_BANK_WIDTH_2
+       select MTD_CFI_I1
+        help
+         Support for parsing CFE image tag and creating MTD partitions on
+         Broadcom BCM63xx boards.
+
 config MTD_DILNETPC
        tristate "CFI Flash device mapped on DIL/Net PC"
        depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
index f216bb5..c7869c7 100644 (file)
@@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_BFIN_ASYNC)  += bfin-async-flash.o
 obj-$(CONFIG_MTD_RBTX4939)     += rbtx4939-flash.o
 obj-$(CONFIG_MTD_VMU)          += vmu-flash.o
 obj-$(CONFIG_MTD_GPIO_ADDR)    += gpio-addr-flash.o
+obj-$(CONFIG_MTD_BCM963XX)     += bcm963xx-flash.o
diff --git a/drivers/mtd/maps/bcm963xx-flash.c b/drivers/mtd/maps/bcm963xx-flash.c
new file mode 100644 (file)
index 0000000..d175c12
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Copyright Â© 2006-2008  Florian Fainelli <florian@openwrt.org>
+ *                       Mike Albon <malbon@openwrt.org>
+ * Copyright Â© 2009-2010  Daniel Dickinson <openwrt@cshore.neomailbox.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/vmalloc.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/mach-bcm63xx/bcm963xx_tag.h>
+
+#define BCM63XX_BUSWIDTH       2               /* Buswidth */
+#define BCM63XX_EXTENDED_SIZE  0xBFC00000      /* Extended flash address */
+
+#define PFX KBUILD_MODNAME ": "
+
+static struct mtd_partition *parsed_parts;
+
+static struct mtd_info *bcm963xx_mtd_info;
+
+static struct map_info bcm963xx_map = {
+       .name           = "bcm963xx",
+       .bankwidth      = BCM63XX_BUSWIDTH,
+};
+
+static int parse_cfe_partitions(struct mtd_info *master,
+                                               struct mtd_partition **pparts)
+{
+       /* CFE, NVRAM and global Linux are always present */
+       int nrparts = 3, curpart = 0;
+       struct bcm_tag *buf;
+       struct mtd_partition *parts;
+       int ret;
+       size_t retlen;
+       unsigned int rootfsaddr, kerneladdr, spareaddr;
+       unsigned int rootfslen, kernellen, sparelen, totallen;
+       int namelen = 0;
+       int i;
+       char *boardid;
+       char *tagversion;
+
+       /* Allocate memory for buffer */
+       buf = vmalloc(sizeof(struct bcm_tag));
+       if (!buf)
+               return -ENOMEM;
+
+       /* Get the tag */
+       ret = master->read(master, master->erasesize, sizeof(struct bcm_tag),
+                                                       &retlen, (void *)buf);
+       if (retlen != sizeof(struct bcm_tag)) {
+               vfree(buf);
+               return -EIO;
+       }
+
+       sscanf(buf->kernel_address, "%u", &kerneladdr);
+       sscanf(buf->kernel_length, "%u", &kernellen);
+       sscanf(buf->total_length, "%u", &totallen);
+       tagversion = &(buf->tag_version[0]);
+       boardid = &(buf->board_id[0]);
+
+       printk(KERN_INFO PFX "CFE boot tag found with version %s "
+                               "and board type %s\n", tagversion, boardid);
+
+       kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
+       rootfsaddr = kerneladdr + kernellen;
+       spareaddr = roundup(totallen, master->erasesize) + master->erasesize;
+       sparelen = master->size - spareaddr - master->erasesize;
+       rootfslen = spareaddr - rootfsaddr;
+
+       /* Determine number of partitions */
+       namelen = 8;
+       if (rootfslen > 0) {
+               nrparts++;
+               namelen += 6;
+       };
+       if (kernellen > 0) {
+               nrparts++;
+               namelen += 6;
+       };
+
+       /* Ask kernel for more memory */
+       parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
+       if (!parts) {
+               vfree(buf);
+               return -ENOMEM;
+       };
+
+       /* Start building partition list */
+       parts[curpart].name = "CFE";
+       parts[curpart].offset = 0;
+       parts[curpart].size = master->erasesize;
+       curpart++;
+
+       if (kernellen > 0) {
+               parts[curpart].name = "kernel";
+               parts[curpart].offset = kerneladdr;
+               parts[curpart].size = kernellen;
+               curpart++;
+       };
+
+       if (rootfslen > 0) {
+               parts[curpart].name = "rootfs";
+               parts[curpart].offset = rootfsaddr;
+               parts[curpart].size = rootfslen;
+               if (sparelen > 0)
+                       parts[curpart].size += sparelen;
+               curpart++;
+       };
+
+       parts[curpart].name = "nvram";
+       parts[curpart].offset = master->size - master->erasesize;
+       parts[curpart].size = master->erasesize;
+
+       /* Global partition "linux" to make easy firmware upgrade */
+       curpart++;
+       parts[curpart].name = "linux";
+       parts[curpart].offset = parts[0].size;
+       parts[curpart].size = master->size - parts[0].size - parts[3].size;
+
+       for (i = 0; i < nrparts; i++)
+               printk(KERN_INFO PFX "Partition %d is %s offset %lx and "
+                                       "length %lx\n", i, parts[i].name,
+                                       (long unsigned int)(parts[i].offset),
+                                       (long unsigned int)(parts[i].size));
+
+       printk(KERN_INFO PFX "Spare partition is %x offset and length %x\n",
+                                                       spareaddr, sparelen);
+       *pparts = parts;
+       vfree(buf);
+
+       return nrparts;
+};
+
+static int bcm963xx_detect_cfe(struct mtd_info *master)
+{
+       int idoffset = 0x4e0;
+       static char idstring[8] = "CFE1CFE1";
+       char buf[9];
+       int ret;
+       size_t retlen;
+
+       ret = master->read(master, idoffset, 8, &retlen, (void *)buf);
+       buf[retlen] = 0;
+       printk(KERN_INFO PFX "Read Signature value of %s\n", buf);
+
+       return strncmp(idstring, buf, 8);
+}
+
+static int bcm963xx_probe(struct platform_device *pdev)
+{
+       int err = 0;
+       int parsed_nr_parts = 0;
+       char *part_type;
+       struct resource *r;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "no resource supplied\n");
+               return -ENODEV;
+       }
+
+       bcm963xx_map.phys = r->start;
+       bcm963xx_map.size = resource_size(r);
+       bcm963xx_map.virt = ioremap(r->start, resource_size(r));
+       if (!bcm963xx_map.virt) {
+               dev_err(&pdev->dev, "failed to ioremap\n");
+               return -EIO;
+       }
+
+       dev_info(&pdev->dev, "0x%08lx at 0x%08x\n",
+                                       bcm963xx_map.size, bcm963xx_map.phys);
+
+       simple_map_init(&bcm963xx_map);
+
+       bcm963xx_mtd_info = do_map_probe("cfi_probe", &bcm963xx_map);
+       if (!bcm963xx_mtd_info) {
+               dev_err(&pdev->dev, "failed to probe using CFI\n");
+               err = -EIO;
+               goto err_probe;
+       }
+
+       bcm963xx_mtd_info->owner = THIS_MODULE;
+
+       /* This is mutually exclusive */
+       if (bcm963xx_detect_cfe(bcm963xx_mtd_info) == 0) {
+               dev_info(&pdev->dev, "CFE bootloader detected\n");
+               if (parsed_nr_parts == 0) {
+                       int ret = parse_cfe_partitions(bcm963xx_mtd_info,
+                                                       &parsed_parts);
+                       if (ret > 0) {
+                               part_type = "CFE";
+                               parsed_nr_parts = ret;
+                       }
+               }
+       } else {
+               dev_info(&pdev->dev, "unsupported bootloader\n");
+               err = -ENODEV;
+               goto err_probe;
+       }
+
+       return add_mtd_partitions(bcm963xx_mtd_info, parsed_parts,
+                                               parsed_nr_parts);
+
+err_probe:
+       iounmap(bcm963xx_map.virt);
+       return err;
+}
+
+static int bcm963xx_remove(struct platform_device *pdev)
+{
+       if (bcm963xx_mtd_info) {
+               del_mtd_partitions(bcm963xx_mtd_info);
+               map_destroy(bcm963xx_mtd_info);
+       }
+
+       if (bcm963xx_map.virt) {
+               iounmap(bcm963xx_map.virt);
+               bcm963xx_map.virt = 0;
+       }
+
+       return 0;
+}
+
+static struct platform_driver bcm63xx_mtd_dev = {
+       .probe  = bcm963xx_probe,
+       .remove = bcm963xx_remove,
+       .driver = {
+               .name   = "bcm963xx-flash",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init bcm963xx_mtd_init(void)
+{
+       return platform_driver_register(&bcm63xx_mtd_dev);
+}
+
+static void __exit bcm963xx_mtd_exit(void)
+{
+       platform_driver_unregister(&bcm63xx_mtd_dev);
+}
+
+module_init(bcm963xx_mtd_init);
+module_exit(bcm963xx_mtd_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot");
+MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");
index 32e89d7..af5707a 100644 (file)
@@ -208,10 +208,14 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
        if (!state)
                return -ENOMEM;
 
+       /*
+        * We cast start/end to known types in the boards file, so cast
+        * away their pointer types here to the known types (gpios->xxx).
+        */
        state->gpio_count     = gpios->end;
-       state->gpio_addrs     = (void *)gpios->start;
+       state->gpio_addrs     = (void *)(unsigned long)gpios->start;
        state->gpio_values    = (void *)(state + 1);
-       state->win_size       = memory->end - memory->start + 1;
+       state->win_size       = resource_size(memory);
        memset(state->gpio_values, 0xff, arr_size);
 
        state->map.name       = DRIVER_NAME;
@@ -221,7 +225,7 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
        state->map.copy_to    = gf_copy_to;
        state->map.bankwidth  = pdata->width;
        state->map.size       = state->win_size * (1 << state->gpio_count);
-       state->map.virt       = (void __iomem *)memory->start;
+       state->map.virt       = ioremap_nocache(memory->start, state->map.size);
        state->map.phys       = NO_XIP;
        state->map.map_priv_1 = (unsigned long)state;
 
index 57a1acf..9170229 100644 (file)
@@ -640,10 +640,6 @@ static int pcmciamtd_config(struct pcmcia_device *link)
        }
        dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name);
        return 0;
-
-       dev_err(&dev->p_dev->dev, "CS Error, exiting\n");
-       pcmciamtd_release(link);
-       return -ENODEV;
 }
 
 
index ec3edf6..9861814 100644 (file)
@@ -50,7 +50,7 @@ static int parse_obsolete_partitions(struct platform_device *dev,
 {
        int i, plen, nr_parts;
        const struct {
-               u32 offset, len;
+               __be32 offset, len;
        } *part;
        const char *names;
 
@@ -69,9 +69,9 @@ static int parse_obsolete_partitions(struct platform_device *dev,
        names = of_get_property(dp, "partition-names", &plen);
 
        for (i = 0; i < nr_parts; i++) {
-               info->parts[i].offset = part->offset;
-               info->parts[i].size   = part->len & ~1;
-               if (part->len & 1) /* bit 0 set signifies read only partition */
+               info->parts[i].offset = be32_to_cpu(part->offset);
+               info->parts[i].size   = be32_to_cpu(part->len) & ~1;
+               if (be32_to_cpu(part->len) & 1) /* bit 0 set signifies read only partition */
                        info->parts[i].mask_flags = MTD_WRITEABLE;
 
                if (names && (plen > 0)) {
@@ -226,11 +226,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
        struct resource res;
        struct of_flash *info;
        const char *probe_type = match->data;
-       const u32 *width;
+       const __be32 *width;
        int err;
        int i;
        int count;
-       const u32 *p;
+       const __be32 *p;
        int reg_tuple_size;
        struct mtd_info **mtd_list = NULL;
        resource_size_t res_size;
@@ -267,9 +267,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
        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;
+                       /*
+                        * Continue with next register tuple if this
+                        * one is not mappable
+                        */
+                       continue;
                }
 
                dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
index 50ab431..f932979 100644 (file)
@@ -133,6 +133,10 @@ static int mtd_blktrans_thread(void *arg)
 
                if (!req && !(req = blk_fetch_request(rq))) {
                        set_current_state(TASK_INTERRUPTIBLE);
+
+                       if (kthread_should_stop())
+                               set_current_state(TASK_RUNNING);
+
                        spin_unlock_irq(rq->queue_lock);
                        schedule();
                        spin_lock_irq(rq->queue_lock);
@@ -176,7 +180,7 @@ static void mtd_blktrans_request(struct request_queue *rq)
 static int blktrans_open(struct block_device *bdev, fmode_t mode)
 {
        struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
-       int ret;
+       int ret = 0;
 
        if (!dev)
                return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
@@ -184,17 +188,17 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
        mutex_lock(&mtd_blkdevs_mutex);
        mutex_lock(&dev->lock);
 
-       if (!dev->mtd) {
-               ret = -ENXIO;
+       if (dev->open++)
                goto unlock;
-       }
 
-       ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;
+       kref_get(&dev->ref);
+       __module_get(dev->tr->owner);
+
+       if (dev->mtd) {
+               ret = dev->tr->open ? dev->tr->open(dev) : 0;
+               __get_mtd_device(dev->mtd);
+       }
 
-       /* Take another reference on the device so it won't go away till
-               last release */
-       if (!ret)
-               kref_get(&dev->ref);
 unlock:
        mutex_unlock(&dev->lock);
        blktrans_dev_put(dev);
@@ -205,7 +209,7 @@ unlock:
 static int blktrans_release(struct gendisk *disk, fmode_t mode)
 {
        struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
-       int ret = -ENXIO;
+       int ret = 0;
 
        if (!dev)
                return ret;
@@ -213,13 +217,16 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode)
        mutex_lock(&mtd_blkdevs_mutex);
        mutex_lock(&dev->lock);
 
-       /* Release one reference, we sure its not the last one here*/
-       kref_put(&dev->ref, blktrans_dev_release);
-
-       if (!dev->mtd)
+       if (--dev->open)
                goto unlock;
 
-       ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0;
+       kref_put(&dev->ref, blktrans_dev_release);
+       module_put(dev->tr->owner);
+
+       if (dev->mtd) {
+               ret = dev->tr->release ? dev->tr->release(dev) : 0;
+               __put_mtd_device(dev->mtd);
+       }
 unlock:
        mutex_unlock(&dev->lock);
        blktrans_dev_put(dev);
@@ -385,9 +392,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
 
        gd->queue = new->rq;
 
-       __get_mtd_device(new->mtd);
-       __module_get(tr->owner);
-
        /* Create processing thread */
        /* TODO: workqueue ? */
        new->thread = kthread_run(mtd_blktrans_thread, new,
@@ -410,8 +414,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
        }
        return 0;
 error4:
-       module_put(tr->owner);
-       __put_mtd_device(new->mtd);
        blk_cleanup_queue(new->rq);
 error3:
        put_disk(new->disk);
@@ -448,17 +450,15 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
        blk_start_queue(old->rq);
        spin_unlock_irqrestore(&old->queue_lock, flags);
 
-       /* Ask trans driver for release to the mtd device */
+       /* If the device is currently open, tell trans driver to close it,
+               then put mtd device, and don't touch it again */
        mutex_lock(&old->lock);
-       if (old->open && old->tr->release) {
-               old->tr->release(old);
-               old->open = 0;
+       if (old->open) {
+               if (old->tr->release)
+                       old->tr->release(old);
+               __put_mtd_device(old->mtd);
        }
 
-       __put_mtd_device(old->mtd);
-       module_put(old->tr->owner);
-
-       /* At that point, we don't touch the mtd anymore */
        old->mtd = NULL;
 
        mutex_unlock(&old->lock);
@@ -508,13 +508,16 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
        mutex_lock(&mtd_table_mutex);
 
        ret = register_blkdev(tr->major, tr->name);
-       if (ret) {
+       if (ret < 0) {
                printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
                       tr->name, tr->major, ret);
                mutex_unlock(&mtd_table_mutex);
                return ret;
        }
 
+       if (ret)
+               tr->major = ret;
+
        tr->blkshift = ffs(tr->blksize) - 1;
 
        INIT_LIST_HEAD(&tr->devs);
index 5ef4548..3eff1e5 100644 (file)
@@ -30,8 +30,9 @@
 #include <linux/backing-dev.h>
 #include <linux/compat.h>
 #include <linux/mount.h>
-
+#include <linux/blkpg.h>
 #include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
 #include <linux/mtd/map.h>
 
 #include <asm/uaccess.h>
@@ -478,6 +479,78 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
        return ret;
 }
 
+/*
+ * Copies (and truncates, if necessary) data from the larger struct,
+ * nand_ecclayout, to the smaller, deprecated layout struct,
+ * nand_ecclayout_user. This is necessary only to suppport the deprecated
+ * API ioctl ECCGETLAYOUT while allowing all new functionality to use
+ * nand_ecclayout flexibly (i.e. the struct may change size in new
+ * releases without requiring major rewrites).
+ */
+static int shrink_ecclayout(const struct nand_ecclayout *from,
+               struct nand_ecclayout_user *to)
+{
+       int i;
+
+       if (!from || !to)
+               return -EINVAL;
+
+       memset(to, 0, sizeof(*to));
+
+       to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
+       for (i = 0; i < to->eccbytes; i++)
+               to->eccpos[i] = from->eccpos[i];
+
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
+               if (from->oobfree[i].length == 0 &&
+                               from->oobfree[i].offset == 0)
+                       break;
+               to->oobavail += from->oobfree[i].length;
+               to->oobfree[i] = from->oobfree[i];
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_MTD_PARTITIONS
+static int mtd_blkpg_ioctl(struct mtd_info *mtd,
+                          struct blkpg_ioctl_arg __user *arg)
+{
+       struct blkpg_ioctl_arg a;
+       struct blkpg_partition p;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       /* Only master mtd device must be used to control partitions */
+       if (!mtd_is_master(mtd))
+               return -EINVAL;
+
+       if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
+               return -EFAULT;
+
+       if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
+               return -EFAULT;
+
+       switch (a.op) {
+       case BLKPG_ADD_PARTITION:
+
+               return mtd_add_partition(mtd, p.devname, p.start, p.length);
+
+       case BLKPG_DEL_PARTITION:
+
+               if (p.pno < 0)
+                       return -EINVAL;
+
+               return mtd_del_partition(mtd, p.pno);
+
+       default:
+               return -EINVAL;
+       }
+}
+#endif
+
+
 static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
 {
        struct mtd_file_info *mfi = file->private_data;
@@ -514,6 +587,9 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
                if (get_user(ur_idx, &(ur->regionindex)))
                        return -EFAULT;
 
+               if (ur_idx >= mtd->numeraseregions)
+                       return -EINVAL;
+
                kr = &(mtd->eraseregions[ur_idx]);
 
                if (put_user(kr->offset, &(ur->offset))
@@ -813,14 +889,23 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
        }
 #endif
 
+       /* This ioctl is being deprecated - it truncates the ecc layout */
        case ECCGETLAYOUT:
        {
+               struct nand_ecclayout_user *usrlay;
+
                if (!mtd->ecclayout)
                        return -EOPNOTSUPP;
 
-               if (copy_to_user(argp, mtd->ecclayout,
-                                sizeof(struct nand_ecclayout)))
-                       return -EFAULT;
+               usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
+               if (!usrlay)
+                       return -ENOMEM;
+
+               shrink_ecclayout(mtd->ecclayout, usrlay);
+
+               if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
+                       ret = -EFAULT;
+               kfree(usrlay);
                break;
        }
 
@@ -856,6 +941,22 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
                break;
        }
 
+#ifdef CONFIG_MTD_PARTITIONS
+       case BLKPG:
+       {
+               ret = mtd_blkpg_ioctl(mtd,
+                     (struct blkpg_ioctl_arg __user *)arg);
+               break;
+       }
+
+       case BLKRRPART:
+       {
+               /* No reread partition feature. Just return ok */
+               ret = 0;
+               break;
+       }
+#endif
+
        default:
                ret = -ENOTTY;
        }
@@ -1034,7 +1135,7 @@ static int mtd_inodefs_get_sb(struct file_system_type *fs_type, int flags,
                                const char *dev_name, void *data,
                                struct vfsmount *mnt)
 {
-        return get_sb_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC,
+       return get_sb_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC,
                              mnt);
 }
 
index dc65585..79e3689 100644 (file)
 #include <linux/kmod.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+#include <linux/err.h>
 
 /* Our partition linked list */
 static LIST_HEAD(mtd_partitions);
+static DEFINE_MUTEX(mtd_partitions_mutex);
 
 /* Our partition node structure */
 struct mtd_part {
@@ -326,6 +328,12 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
        return res;
 }
 
+static inline void free_partition(struct mtd_part *p)
+{
+       kfree(p->mtd.name);
+       kfree(p);
+}
+
 /*
  * This function unregisters and destroy all slave MTD objects which are
  * attached to the given master MTD object.
@@ -334,33 +342,42 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
 int del_mtd_partitions(struct mtd_info *master)
 {
        struct mtd_part *slave, *next;
+       int ret, err = 0;
 
+       mutex_lock(&mtd_partitions_mutex);
        list_for_each_entry_safe(slave, next, &mtd_partitions, list)
                if (slave->master == master) {
+                       ret = del_mtd_device(&slave->mtd);
+                       if (ret < 0) {
+                               err = ret;
+                               continue;
+                       }
                        list_del(&slave->list);
-                       del_mtd_device(&slave->mtd);
-                       kfree(slave);
+                       free_partition(slave);
                }
+       mutex_unlock(&mtd_partitions_mutex);
 
-       return 0;
+       return err;
 }
 EXPORT_SYMBOL(del_mtd_partitions);
 
-static struct mtd_part *add_one_partition(struct mtd_info *master,
-               const struct mtd_partition *part, int partno,
-               uint64_t cur_offset)
+static struct mtd_part *allocate_partition(struct mtd_info *master,
+                       const struct mtd_partition *part, int partno,
+                       uint64_t cur_offset)
 {
        struct mtd_part *slave;
+       char *name;
 
        /* allocate the partition structure */
        slave = kzalloc(sizeof(*slave), GFP_KERNEL);
-       if (!slave) {
+       name = kstrdup(part->name, GFP_KERNEL);
+       if (!name || !slave) {
                printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
-                       master->name);
-               del_mtd_partitions(master);
-               return NULL;
+                      master->name);
+               kfree(name);
+               kfree(slave);
+               return ERR_PTR(-ENOMEM);
        }
-       list_add(&slave->list, &mtd_partitions);
 
        /* set up the MTD object for this partition */
        slave->mtd.type = master->type;
@@ -371,7 +388,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
        slave->mtd.oobavail = master->oobavail;
        slave->mtd.subpage_sft = master->subpage_sft;
 
-       slave->mtd.name = part->name;
+       slave->mtd.name = name;
        slave->mtd.owner = master->owner;
        slave->mtd.backing_dev_info = master->backing_dev_info;
 
@@ -518,12 +535,89 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
        }
 
 out_register:
-       /* register our partition */
-       add_mtd_device(&slave->mtd);
-
        return slave;
 }
 
+int mtd_add_partition(struct mtd_info *master, char *name,
+                     long long offset, long long length)
+{
+       struct mtd_partition part;
+       struct mtd_part *p, *new;
+       uint64_t start, end;
+       int ret = 0;
+
+       /* the direct offset is expected */
+       if (offset == MTDPART_OFS_APPEND ||
+           offset == MTDPART_OFS_NXTBLK)
+               return -EINVAL;
+
+       if (length == MTDPART_SIZ_FULL)
+               length = master->size - offset;
+
+       if (length <= 0)
+               return -EINVAL;
+
+       part.name = name;
+       part.size = length;
+       part.offset = offset;
+       part.mask_flags = 0;
+       part.ecclayout = NULL;
+
+       new = allocate_partition(master, &part, -1, offset);
+       if (IS_ERR(new))
+               return PTR_ERR(new);
+
+       start = offset;
+       end = offset + length;
+
+       mutex_lock(&mtd_partitions_mutex);
+       list_for_each_entry(p, &mtd_partitions, list)
+               if (p->master == master) {
+                       if ((start >= p->offset) &&
+                           (start < (p->offset + p->mtd.size)))
+                               goto err_inv;
+
+                       if ((end >= p->offset) &&
+                           (end < (p->offset + p->mtd.size)))
+                               goto err_inv;
+               }
+
+       list_add(&new->list, &mtd_partitions);
+       mutex_unlock(&mtd_partitions_mutex);
+
+       add_mtd_device(&new->mtd);
+
+       return ret;
+err_inv:
+       mutex_unlock(&mtd_partitions_mutex);
+       free_partition(new);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtd_add_partition);
+
+int mtd_del_partition(struct mtd_info *master, int partno)
+{
+       struct mtd_part *slave, *next;
+       int ret = -EINVAL;
+
+       mutex_lock(&mtd_partitions_mutex);
+       list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+               if ((slave->master == master) &&
+                   (slave->mtd.index == partno)) {
+                       ret = del_mtd_device(&slave->mtd);
+                       if (ret < 0)
+                               break;
+
+                       list_del(&slave->list);
+                       free_partition(slave);
+                       break;
+               }
+       mutex_unlock(&mtd_partitions_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mtd_del_partition);
+
 /*
  * This function, given a master MTD object and a partition table, creates
  * and registers slave MTD objects which are bound to the master according to
@@ -544,9 +638,16 @@ int add_mtd_partitions(struct mtd_info *master,
        printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
 
        for (i = 0; i < nbparts; i++) {
-               slave = add_one_partition(master, parts + i, i, cur_offset);
-               if (!slave)
-                       return -ENOMEM;
+               slave = allocate_partition(master, parts + i, i, cur_offset);
+               if (IS_ERR(slave))
+                       return PTR_ERR(slave);
+
+               mutex_lock(&mtd_partitions_mutex);
+               list_add(&slave->list, &mtd_partitions);
+               mutex_unlock(&mtd_partitions_mutex);
+
+               add_mtd_device(&slave->mtd);
+
                cur_offset = slave->offset + slave->mtd.size;
        }
 
@@ -618,3 +719,20 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
        return ret;
 }
 EXPORT_SYMBOL_GPL(parse_mtd_partitions);
+
+int mtd_is_master(struct mtd_info *mtd)
+{
+       struct mtd_part *part;
+       int nopart = 0;
+
+       mutex_lock(&mtd_partitions_mutex);
+       list_for_each_entry(part, &mtd_partitions, list)
+               if (&part->mtd == mtd) {
+                       nopart = 1;
+                       break;
+               }
+       mutex_unlock(&mtd_partitions_mutex);
+
+       return nopart;
+}
+EXPORT_SYMBOL_GPL(mtd_is_master);
index 8b4b67c..8229802 100644 (file)
@@ -400,13 +400,6 @@ config MTD_NAND_PXA3xx
          This enables the driver for the NAND flash device found on
          PXA3xx processors
 
-config MTD_NAND_PXA3xx_BUILTIN
-       bool "Use builtin definitions for some NAND chips (deprecated)"
-       depends on MTD_NAND_PXA3xx
-       help
-         This enables builtin definitions for some NAND chips. This
-         is deprecated in favor of platform specific data.
-
 config MTD_NAND_CM_X270
        tristate "Support for NAND Flash on CM-X270 modules"
        depends on MACH_ARMCORE
@@ -458,6 +451,7 @@ config MTD_NAND_ORION
 config MTD_NAND_FSL_ELBC
        tristate "NAND support for Freescale eLBC controllers"
        depends on PPC_OF
+       select FSL_LBC
        help
          Various Freescale chips, including the 8313, include a NAND Flash
          Controller Module with built-in hardware ECC capabilities.
@@ -531,4 +525,11 @@ config MTD_NAND_JZ4740
        help
                Enables support for NAND Flash on JZ4740 SoC based boards.
 
+config MTD_NAND_FSMC
+       tristate "Support for NAND on ST Micros FSMC"
+       depends on PLAT_SPEAR || PLAT_NOMADIK || MACH_U300
+       help
+         Enables support for NAND Flash chips on the ST Microelectronics
+         Flexible Static Memory Controller (FSMC)
+
 endif # MTD_NAND
index ac83dcd..8ad6fae 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
 obj-$(CONFIG_MTD_NAND_S3C2410)         += s3c2410.o
 obj-$(CONFIG_MTD_NAND_DAVINCI)         += davinci_nand.o
 obj-$(CONFIG_MTD_NAND_DISKONCHIP)      += diskonchip.o
+obj-$(CONFIG_MTD_NAND_FSMC)            += fsmc_nand.o
 obj-$(CONFIG_MTD_NAND_H1900)           += h1910.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)       += rtc_from4.o
 obj-$(CONFIG_MTD_NAND_SHARPSL)         += sharpsl.o
index 6fbeefa..79947be 100644 (file)
@@ -110,15 +110,6 @@ static const unsigned short bfin_nfc_pin_req[] =
         0};
 
 #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
-static uint8_t bbt_pattern[] = { 0xff };
-
-static struct nand_bbt_descr bootrom_bbt = {
-       .options = 0,
-       .offs = 63,
-       .len = 1,
-       .pattern = bbt_pattern,
-};
-
 static struct nand_ecclayout bootrom_ecclayout = {
        .eccbytes = 24,
        .eccpos = {
@@ -809,7 +800,6 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev)
        /* setup hardware ECC data struct */
        if (hardware_ecc) {
 #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
-               chip->badblock_pattern = &bootrom_bbt;
                chip->ecc.layout = &bootrom_ecclayout;
 #endif
                chip->read_buf      = bf5xx_nand_dma_read_buf;
@@ -830,6 +820,10 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev)
                goto out_err_nand_scan;
        }
 
+#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
+       chip->badblockpos = 63;
+#endif
+
        /* add NAND partition */
        bf5xx_nand_add_partition(info);
 
index 2ac7367..ad51f8e 100644 (file)
@@ -313,7 +313,7 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd,
        u32 syndrome[4];
        u32 ecc_state;
        unsigned num_errors, corrected;
-       unsigned long timeo = jiffies + msecs_to_jiffies(100);
+       unsigned long timeo;
 
        /* All bytes 0xff?  It's an erased page; ignore its ECC. */
        for (i = 0; i < 10; i++) {
@@ -369,9 +369,11 @@ compare:
         * after setting the 4BITECC_ADD_CALC_START bit. So if you immediately
         * begin trying to poll for the state, you may fall right out of your
         * loop without any of the correction calculations having taken place.
-        * The recommendation from the hardware team is to wait till ECC_STATE
-        * reads less than 4, which means ECC HW has entered correction state.
+        * The recommendation from the hardware team is to initially delay as
+        * long as ECC_STATE reads less than 4. After that, ECC HW has entered
+        * correction state.
         */
+       timeo = jiffies + usecs_to_jiffies(100);
        do {
                ecc_state = (davinci_nand_readl(info,
                                NANDFSR_OFFSET) >> 8) & 0x0f;
@@ -749,6 +751,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
                 * breaks userspace ioctl interface with mtd-utils. Once we
                 * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used
                 * for the 4KiB page chips.
+                *
+                * TODO: Note that nand_ecclayout has now been expanded and can
+                *  hold plenty of OOB entries.
                 */
                dev_warn(&pdev->dev, "no 4-bit ECC support yet "
                                "for 4KiB-page NAND\n");
index 532fe07..8c8d3c8 100644 (file)
@@ -1292,6 +1292,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
                read_status(denali);
                break;
        case NAND_CMD_READID:
+       case NAND_CMD_PARAM:
                reset_buf(denali);
                /*sometimes ManufactureId read from register is not right
                 * e.g. some of Micron MT29F32G08QAA MLC NAND chips
index 80de0bf..c141b07 100644 (file)
@@ -1,9 +1,11 @@
 /* Freescale Enhanced Local Bus Controller NAND driver
  *
- * Copyright (c) 2006-2007 Freescale Semiconductor
+ * Copyright Â© 2006-2007, 2010 Freescale Semiconductor
  *
  * Authors: Nick Spence <nick.spence@freescale.com>,
  *          Scott Wood <scottwood@freescale.com>
+ *          Jack Lan <jack.lan@freescale.com>
+ *          Roy Zang <tie-fei.zang@freescale.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
@@ -27,6 +29,7 @@
 #include <linux/string.h>
 #include <linux/ioport.h>
 #include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 
 #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
 #define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
 
-struct fsl_elbc_ctrl;
-
 /* mtd information per set */
 
 struct fsl_elbc_mtd {
        struct mtd_info mtd;
        struct nand_chip chip;
-       struct fsl_elbc_ctrl *ctrl;
+       struct fsl_lbc_ctrl *ctrl;
 
        struct device *dev;
        int bank;               /* Chip select bank number           */
@@ -58,18 +59,12 @@ struct fsl_elbc_mtd {
        unsigned int fmr;       /* FCM Flash Mode Register value     */
 };
 
-/* overview of the fsl elbc controller */
+/* Freescale eLBC FCM controller infomation */
 
-struct fsl_elbc_ctrl {
+struct fsl_elbc_fcm_ctrl {
        struct nand_hw_control controller;
        struct fsl_elbc_mtd *chips[MAX_BANKS];
 
-       /* device info */
-       struct device *dev;
-       struct fsl_lbc_regs __iomem *regs;
-       int irq;
-       wait_queue_head_t irq_wait;
-       unsigned int irq_status; /* status read from LTESR by irq handler */
        u8 __iomem *addr;        /* Address of assigned FCM buffer        */
        unsigned int page;       /* Last page written to / read from      */
        unsigned int read_bytes; /* Number of bytes read during command   */
@@ -79,6 +74,7 @@ struct fsl_elbc_ctrl {
        unsigned int mdr;        /* UPM/FCM Data Register value           */
        unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
        unsigned int oob;        /* Non zero if operating on OOB data     */
+       unsigned int counter;    /* counter for the initializations       */
        char *oob_poi;           /* Place to write ECC after read back    */
 };
 
@@ -164,11 +160,12 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_lbc_ctrl *ctrl = priv->ctrl;
        struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
        int buf_num;
 
-       ctrl->page = page_addr;
+       elbc_fcm_ctrl->page = page_addr;
 
        out_be32(&lbc->fbar,
                 page_addr >> (chip->phys_erase_shift - chip->page_shift));
@@ -185,16 +182,18 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
                buf_num = page_addr & 7;
        }
 
-       ctrl->addr = priv->vbase + buf_num * 1024;
-       ctrl->index = column;
+       elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
+       elbc_fcm_ctrl->index = column;
 
        /* for OOB data point to the second half of the buffer */
        if (oob)
-               ctrl->index += priv->page_size ? 2048 : 512;
+               elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
 
-       dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "
+       dev_vdbg(priv->dev, "set_addr: bank=%d, "
+                           "elbc_fcm_ctrl->addr=0x%p (0x%p), "
                            "index %x, pes %d ps %d\n",
-                buf_num, ctrl->addr, priv->vbase, ctrl->index,
+                buf_num, elbc_fcm_ctrl->addr, priv->vbase,
+                elbc_fcm_ctrl->index,
                 chip->phys_erase_shift, chip->page_shift);
 }
 
@@ -205,18 +204,19 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
        struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
 
        /* Setup the FMR[OP] to execute without write protection */
        out_be32(&lbc->fmr, priv->fmr | 3);
-       if (ctrl->use_mdr)
-               out_be32(&lbc->mdr, ctrl->mdr);
+       if (elbc_fcm_ctrl->use_mdr)
+               out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
 
-       dev_vdbg(ctrl->dev,
+       dev_vdbg(priv->dev,
                 "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
                 in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
-       dev_vdbg(ctrl->dev,
+       dev_vdbg(priv->dev,
                 "fsl_elbc_run_command: fbar=%08x fpar=%08x "
                 "fbcr=%08x bank=%d\n",
                 in_be32(&lbc->fbar), in_be32(&lbc->fpar),
@@ -229,19 +229,18 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
        /* wait for FCM complete flag or timeout */
        wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
                           FCM_TIMEOUT_MSECS * HZ/1000);
-       ctrl->status = ctrl->irq_status;
-
+       elbc_fcm_ctrl->status = ctrl->irq_status;
        /* store mdr value in case it was needed */
-       if (ctrl->use_mdr)
-               ctrl->mdr = in_be32(&lbc->mdr);
+       if (elbc_fcm_ctrl->use_mdr)
+               elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
 
-       ctrl->use_mdr = 0;
+       elbc_fcm_ctrl->use_mdr = 0;
 
-       if (ctrl->status != LTESR_CC) {
-               dev_info(ctrl->dev,
+       if (elbc_fcm_ctrl->status != LTESR_CC) {
+               dev_info(priv->dev,
                         "command failed: fir %x fcr %x status %x mdr %x\n",
                         in_be32(&lbc->fir), in_be32(&lbc->fcr),
-                        ctrl->status, ctrl->mdr);
+                        elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
                return -EIO;
        }
 
@@ -251,7 +250,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
 static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
 {
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_lbc_ctrl *ctrl = priv->ctrl;
        struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
 
        if (priv->page_size) {
@@ -284,15 +283,16 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
        struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
 
-       ctrl->use_mdr = 0;
+       elbc_fcm_ctrl->use_mdr = 0;
 
        /* clear the read buffer */
-       ctrl->read_bytes = 0;
+       elbc_fcm_ctrl->read_bytes = 0;
        if (command != NAND_CMD_PAGEPROG)
-               ctrl->index = 0;
+               elbc_fcm_ctrl->index = 0;
 
        switch (command) {
        /* READ0 and READ1 read the entire buffer to use hardware ECC. */
@@ -301,7 +301,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        /* fall-through */
        case NAND_CMD_READ0:
-               dev_dbg(ctrl->dev,
+               dev_dbg(priv->dev,
                        "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
                        " 0x%x, column: 0x%x.\n", page_addr, column);
 
@@ -309,8 +309,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
                set_addr(mtd, 0, page_addr, 0);
 
-               ctrl->read_bytes = mtd->writesize + mtd->oobsize;
-               ctrl->index += column;
+               elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+               elbc_fcm_ctrl->index += column;
 
                fsl_elbc_do_read(chip, 0);
                fsl_elbc_run_command(mtd);
@@ -318,14 +318,14 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        /* READOOB reads only the OOB because no ECC is performed. */
        case NAND_CMD_READOOB:
-               dev_vdbg(ctrl->dev,
+               dev_vdbg(priv->dev,
                         "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
                         " 0x%x, column: 0x%x.\n", page_addr, column);
 
                out_be32(&lbc->fbcr, mtd->oobsize - column);
                set_addr(mtd, column, page_addr, 1);
 
-               ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+               elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
 
                fsl_elbc_do_read(chip, 1);
                fsl_elbc_run_command(mtd);
@@ -333,7 +333,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        /* READID must read all 5 possible bytes while CEB is active */
        case NAND_CMD_READID:
-               dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
+               dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
 
                out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
                                    (FIR_OP_UA  << FIR_OP1_SHIFT) |
@@ -341,9 +341,9 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
                /* 5 bytes for manuf, device and exts */
                out_be32(&lbc->fbcr, 5);
-               ctrl->read_bytes = 5;
-               ctrl->use_mdr = 1;
-               ctrl->mdr = 0;
+               elbc_fcm_ctrl->read_bytes = 5;
+               elbc_fcm_ctrl->use_mdr = 1;
+               elbc_fcm_ctrl->mdr = 0;
 
                set_addr(mtd, 0, 0, 0);
                fsl_elbc_run_command(mtd);
@@ -351,7 +351,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        /* ERASE1 stores the block and page address */
        case NAND_CMD_ERASE1:
-               dev_vdbg(ctrl->dev,
+               dev_vdbg(priv->dev,
                         "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
                         "page_addr: 0x%x.\n", page_addr);
                set_addr(mtd, 0, page_addr, 0);
@@ -359,7 +359,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        /* ERASE2 uses the block and page address from ERASE1 */
        case NAND_CMD_ERASE2:
-               dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
+               dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
 
                out_be32(&lbc->fir,
                         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
@@ -374,8 +374,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                         (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
 
                out_be32(&lbc->fbcr, 0);
-               ctrl->read_bytes = 0;
-               ctrl->use_mdr = 1;
+               elbc_fcm_ctrl->read_bytes = 0;
+               elbc_fcm_ctrl->use_mdr = 1;
 
                fsl_elbc_run_command(mtd);
                return;
@@ -383,14 +383,12 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
        /* SEQIN sets up the addr buffer and all registers except the length */
        case NAND_CMD_SEQIN: {
                __be32 fcr;
-               dev_vdbg(ctrl->dev,
-                        "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
+               dev_vdbg(priv->dev,
+                        "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
                         "page_addr: 0x%x, column: 0x%x.\n",
                         page_addr, column);
 
-               ctrl->column = column;
-               ctrl->oob = 0;
-               ctrl->use_mdr = 1;
+               elbc_fcm_ctrl->use_mdr = 1;
 
                fcr = (NAND_CMD_STATUS   << FCR_CMD1_SHIFT) |
                      (NAND_CMD_SEQIN    << FCR_CMD2_SHIFT) |
@@ -420,7 +418,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                                /* OOB area --> READOOB */
                                column -= mtd->writesize;
                                fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
-                               ctrl->oob = 1;
+                               elbc_fcm_ctrl->oob = 1;
                        } else {
                                WARN_ON(column != 0);
                                /* First 256 bytes --> READ0 */
@@ -429,24 +427,24 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                }
 
                out_be32(&lbc->fcr, fcr);
-               set_addr(mtd, column, page_addr, ctrl->oob);
+               set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
                return;
        }
 
        /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
        case NAND_CMD_PAGEPROG: {
                int full_page;
-               dev_vdbg(ctrl->dev,
+               dev_vdbg(priv->dev,
                         "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
-                        "writing %d bytes.\n", ctrl->index);
+                        "writing %d bytes.\n", elbc_fcm_ctrl->index);
 
                /* if the write did not start at 0 or is not a full page
                 * then set the exact length, otherwise use a full page
                 * write so the HW generates the ECC.
                 */
-               if (ctrl->oob || ctrl->column != 0 ||
-                   ctrl->index != mtd->writesize + mtd->oobsize) {
-                       out_be32(&lbc->fbcr, ctrl->index);
+               if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
+                   elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) {
+                       out_be32(&lbc->fbcr, elbc_fcm_ctrl->index);
                        full_page = 0;
                } else {
                        out_be32(&lbc->fbcr, 0);
@@ -458,21 +456,21 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                /* Read back the page in order to fill in the ECC for the
                 * caller.  Is this really needed?
                 */
-               if (full_page && ctrl->oob_poi) {
+               if (full_page && elbc_fcm_ctrl->oob_poi) {
                        out_be32(&lbc->fbcr, 3);
                        set_addr(mtd, 6, page_addr, 1);
 
-                       ctrl->read_bytes = mtd->writesize + 9;
+                       elbc_fcm_ctrl->read_bytes = mtd->writesize + 9;
 
                        fsl_elbc_do_read(chip, 1);
                        fsl_elbc_run_command(mtd);
 
-                       memcpy_fromio(ctrl->oob_poi + 6,
-                                     &ctrl->addr[ctrl->index], 3);
-                       ctrl->index += 3;
+                       memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6,
+                               &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3);
+                       elbc_fcm_ctrl->index += 3;
                }
 
-               ctrl->oob_poi = NULL;
+               elbc_fcm_ctrl->oob_poi = NULL;
                return;
        }
 
@@ -485,26 +483,26 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
                out_be32(&lbc->fbcr, 1);
                set_addr(mtd, 0, 0, 0);
-               ctrl->read_bytes = 1;
+               elbc_fcm_ctrl->read_bytes = 1;
 
                fsl_elbc_run_command(mtd);
 
                /* The chip always seems to report that it is
                 * write-protected, even when it is not.
                 */
-               setbits8(ctrl->addr, NAND_STATUS_WP);
+               setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
                return;
 
        /* RESET without waiting for the ready line */
        case NAND_CMD_RESET:
-               dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
+               dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
                out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
                out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
                fsl_elbc_run_command(mtd);
                return;
 
        default:
-               dev_err(ctrl->dev,
+               dev_err(priv->dev,
                        "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
                        command);
        }
@@ -524,24 +522,24 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
        unsigned int bufsize = mtd->writesize + mtd->oobsize;
 
        if (len <= 0) {
-               dev_err(ctrl->dev, "write_buf of %d bytes", len);
-               ctrl->status = 0;
+               dev_err(priv->dev, "write_buf of %d bytes", len);
+               elbc_fcm_ctrl->status = 0;
                return;
        }
 
-       if ((unsigned int)len > bufsize - ctrl->index) {
-               dev_err(ctrl->dev,
+       if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
+               dev_err(priv->dev,
                        "write_buf beyond end of buffer "
                        "(%d requested, %u available)\n",
-                       len, bufsize - ctrl->index);
-               len = bufsize - ctrl->index;
+                       len, bufsize - elbc_fcm_ctrl->index);
+               len = bufsize - elbc_fcm_ctrl->index;
        }
 
-       memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
+       memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
        /*
         * This is workaround for the weird elbc hangs during nand write,
         * Scott Wood says: "...perhaps difference in how long it takes a
@@ -549,9 +547,9 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
         * is causing problems, and sync isn't helping for some reason."
         * Reading back the last byte helps though.
         */
-       in_8(&ctrl->addr[ctrl->index] + len - 1);
+       in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
 
-       ctrl->index += len;
+       elbc_fcm_ctrl->index += len;
 }
 
 /*
@@ -562,13 +560,13 @@ static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 
        /* If there are still bytes in the FCM, then use the next byte. */
-       if (ctrl->index < ctrl->read_bytes)
-               return in_8(&ctrl->addr[ctrl->index++]);
+       if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
+               return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
 
-       dev_err(ctrl->dev, "read_byte beyond end of buffer\n");
+       dev_err(priv->dev, "read_byte beyond end of buffer\n");
        return ERR_BYTE;
 }
 
@@ -579,18 +577,19 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
        int avail;
 
        if (len < 0)
                return;
 
-       avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
-       memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
-       ctrl->index += avail;
+       avail = min((unsigned int)len,
+                       elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
+       memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
+       elbc_fcm_ctrl->index += avail;
 
        if (len > avail)
-               dev_err(ctrl->dev,
+               dev_err(priv->dev,
                        "read_buf beyond end of buffer "
                        "(%d requested, %d available)\n",
                        len, avail);
@@ -603,30 +602,32 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
        int i;
 
        if (len < 0) {
-               dev_err(ctrl->dev, "write_buf of %d bytes", len);
+               dev_err(priv->dev, "write_buf of %d bytes", len);
                return -EINVAL;
        }
 
-       if ((unsigned int)len > ctrl->read_bytes - ctrl->index) {
-               dev_err(ctrl->dev,
-                       "verify_buf beyond end of buffer "
-                       "(%d requested, %u available)\n",
-                       len, ctrl->read_bytes - ctrl->index);
+       if ((unsigned int)len >
+                       elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index) {
+               dev_err(priv->dev,
+                       "verify_buf beyond end of buffer "
+                       "(%d requested, %u available)\n",
+                       len, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
 
-               ctrl->index = ctrl->read_bytes;
+               elbc_fcm_ctrl->index = elbc_fcm_ctrl->read_bytes;
                return -EINVAL;
        }
 
        for (i = 0; i < len; i++)
-               if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
+               if (in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index + i])
+                               != buf[i])
                        break;
 
-       ctrl->index += len;
-       return i == len && ctrl->status == LTESR_CC ? 0 : -EIO;
+       elbc_fcm_ctrl->index += len;
+       return i == len && elbc_fcm_ctrl->status == LTESR_CC ? 0 : -EIO;
 }
 
 /* This function is called after Program and Erase Operations to
@@ -635,22 +636,22 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
 static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
 {
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 
-       if (ctrl->status != LTESR_CC)
+       if (elbc_fcm_ctrl->status != LTESR_CC)
                return NAND_STATUS_FAIL;
 
        /* The chip always seems to report that it is
         * write-protected, even when it is not.
         */
-       return (ctrl->mdr & 0xff) | NAND_STATUS_WP;
+       return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
 }
 
 static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_lbc_ctrl *ctrl = priv->ctrl;
        struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
        unsigned int al;
 
@@ -665,41 +666,41 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
        priv->fmr |= (12 << FMR_CWTO_SHIFT) |  /* Timeout > 12 ms */
                     (al << FMR_AL_SHIFT);
 
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
                chip->numchips);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
                chip->chipsize);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
                chip->pagemask);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
                chip->chip_delay);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
                chip->badblockpos);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
                chip->chip_shift);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
                chip->page_shift);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
                chip->phys_erase_shift);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
                chip->ecclayout);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
                chip->ecc.mode);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
                chip->ecc.steps);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
                chip->ecc.bytes);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
                chip->ecc.total);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
                chip->ecc.layout);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
+       dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
+       dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
                mtd->erasesize);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
                mtd->writesize);
-       dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
+       dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
                mtd->oobsize);
 
        /* adjust Option Register and ECC to match Flash page size */
@@ -719,7 +720,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
                        chip->badblock_pattern = &largepage_memorybased;
                }
        } else {
-               dev_err(ctrl->dev,
+               dev_err(priv->dev,
                        "fsl_elbc_init: page size %d is not supported\n",
                        mtd->writesize);
                return -1;
@@ -750,18 +751,19 @@ static void fsl_elbc_write_page(struct mtd_info *mtd,
                                 const uint8_t *buf)
 {
        struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 
        fsl_elbc_write_buf(mtd, buf, mtd->writesize);
        fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
 
-       ctrl->oob_poi = chip->oob_poi;
+       elbc_fcm_ctrl->oob_poi = chip->oob_poi;
 }
 
 static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 {
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+       struct fsl_lbc_ctrl *ctrl = priv->ctrl;
        struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
        struct nand_chip *chip = &priv->chip;
 
        dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
@@ -790,7 +792,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
        chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
                        NAND_USE_FLASH_BBT;
 
-       chip->controller = &ctrl->controller;
+       chip->controller = &elbc_fcm_ctrl->controller;
        chip->priv = priv;
 
        chip->ecc.read_page = fsl_elbc_read_page;
@@ -815,8 +817,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 
 static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
 {
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
-
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
        nand_release(&priv->mtd);
 
        kfree(priv->mtd.name);
@@ -824,18 +825,21 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
        if (priv->vbase)
                iounmap(priv->vbase);
 
-       ctrl->chips[priv->bank] = NULL;
+       elbc_fcm_ctrl->chips[priv->bank] = NULL;
        kfree(priv);
-
+       kfree(elbc_fcm_ctrl);
        return 0;
 }
 
-static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
-                                        struct device_node *node)
+static DEFINE_MUTEX(fsl_elbc_nand_mutex);
+
+static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
 {
-       struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+       struct fsl_lbc_regs __iomem *lbc;
        struct fsl_elbc_mtd *priv;
        struct resource res;
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
+
 #ifdef CONFIG_MTD_PARTITIONS
        static const char *part_probe_types[]
                = { "cmdlinepart", "RedBoot", NULL };
@@ -843,11 +847,18 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
 #endif
        int ret;
        int bank;
+       struct device *dev;
+       struct device_node *node = pdev->dev.of_node;
+
+       if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+               return -ENODEV;
+       lbc = fsl_lbc_ctrl_dev->regs;
+       dev = fsl_lbc_ctrl_dev->dev;
 
        /* get, allocate and map the memory resource */
        ret = of_address_to_resource(node, 0, &res);
        if (ret) {
-               dev_err(ctrl->dev, "failed to get resource\n");
+               dev_err(dev, "failed to get resource\n");
                return ret;
        }
 
@@ -857,11 +868,11 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
                    (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&
                    (in_be32(&lbc->bank[bank].br) &
                     in_be32(&lbc->bank[bank].or) & BR_BA)
-                    == res.start)
+                    == fsl_lbc_addr(res.start))
                        break;
 
        if (bank >= MAX_BANKS) {
-               dev_err(ctrl->dev, "address did not match any chip selects\n");
+               dev_err(dev, "address did not match any chip selects\n");
                return -ENODEV;
        }
 
@@ -869,14 +880,33 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
        if (!priv)
                return -ENOMEM;
 
-       ctrl->chips[bank] = priv;
+       mutex_lock(&fsl_elbc_nand_mutex);
+       if (!fsl_lbc_ctrl_dev->nand) {
+               elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
+               if (!elbc_fcm_ctrl) {
+                       dev_err(dev, "failed to allocate memory\n");
+                       mutex_unlock(&fsl_elbc_nand_mutex);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+               elbc_fcm_ctrl->counter++;
+
+               spin_lock_init(&elbc_fcm_ctrl->controller.lock);
+               init_waitqueue_head(&elbc_fcm_ctrl->controller.wq);
+               fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
+       } else {
+               elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
+       }
+       mutex_unlock(&fsl_elbc_nand_mutex);
+
+       elbc_fcm_ctrl->chips[bank] = priv;
        priv->bank = bank;
-       priv->ctrl = ctrl;
-       priv->dev = ctrl->dev;
+       priv->ctrl = fsl_lbc_ctrl_dev;
+       priv->dev = dev;
 
        priv->vbase = ioremap(res.start, resource_size(&res));
        if (!priv->vbase) {
-               dev_err(ctrl->dev, "failed to map chip region\n");
+               dev_err(dev, "failed to map chip region\n");
                ret = -ENOMEM;
                goto err;
        }
@@ -933,171 +963,53 @@ err:
        return ret;
 }
 
-static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
+static int fsl_elbc_nand_remove(struct platform_device *pdev)
 {
-       struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
-
-       /*
-        * NAND transactions can tie up the bus for a long time, so set the
-        * bus timeout to max by clearing LBCR[BMT] (highest base counter
-        * value) and setting LBCR[BMTPS] to the highest prescaler value.
-        */
-       clrsetbits_be32(&lbc->lbcr, LBCR_BMT, 15);
-
-       /* clear event registers */
-       setbits32(&lbc->ltesr, LTESR_NAND_MASK);
-       out_be32(&lbc->lteatr, 0);
-
-       /* Enable interrupts for any detected events */
-       out_be32(&lbc->lteir, LTESR_NAND_MASK);
-
-       ctrl->read_bytes = 0;
-       ctrl->index = 0;
-       ctrl->addr = NULL;
-
-       return 0;
-}
-
-static int fsl_elbc_ctrl_remove(struct platform_device *ofdev)
-{
-       struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
        int i;
-
+       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
        for (i = 0; i < MAX_BANKS; i++)
-               if (ctrl->chips[i])
-                       fsl_elbc_chip_remove(ctrl->chips[i]);
-
-       if (ctrl->irq)
-               free_irq(ctrl->irq, ctrl);
-
-       if (ctrl->regs)
-               iounmap(ctrl->regs);
-
-       dev_set_drvdata(&ofdev->dev, NULL);
-       kfree(ctrl);
-       return 0;
-}
-
-/* NOTE: This interrupt is also used to report other localbus events,
- * such as transaction errors on other chipselects.  If we want to
- * capture those, we'll need to move the IRQ code into a shared
- * LBC driver.
- */
-
-static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data)
-{
-       struct fsl_elbc_ctrl *ctrl = data;
-       struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
-       __be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK;
-
-       if (status) {
-               out_be32(&lbc->ltesr, status);
-               out_be32(&lbc->lteatr, 0);
-
-               ctrl->irq_status = status;
-               smp_wmb();
-               wake_up(&ctrl->irq_wait);
-
-               return IRQ_HANDLED;
+               if (elbc_fcm_ctrl->chips[i])
+                       fsl_elbc_chip_remove(elbc_fcm_ctrl->chips[i]);
+
+       mutex_lock(&fsl_elbc_nand_mutex);
+       elbc_fcm_ctrl->counter--;
+       if (!elbc_fcm_ctrl->counter) {
+               fsl_lbc_ctrl_dev->nand = NULL;
+               kfree(elbc_fcm_ctrl);
        }
-
-       return IRQ_NONE;
-}
-
-/* fsl_elbc_ctrl_probe
- *
- * called by device layer when it finds a device matching
- * one our driver can handled. This code allocates all of
- * the resources needed for the controller only.  The
- * resources for the NAND banks themselves are allocated
- * in the chip probe function.
-*/
-
-static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev,
-                                         const struct of_device_id *match)
-{
-       struct device_node *child;
-       struct fsl_elbc_ctrl *ctrl;
-       int ret;
-
-       ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
-       if (!ctrl)
-               return -ENOMEM;
-
-       dev_set_drvdata(&ofdev->dev, ctrl);
-
-       spin_lock_init(&ctrl->controller.lock);
-       init_waitqueue_head(&ctrl->controller.wq);
-       init_waitqueue_head(&ctrl->irq_wait);
-
-       ctrl->regs = of_iomap(ofdev->dev.of_node, 0);
-       if (!ctrl->regs) {
-               dev_err(&ofdev->dev, "failed to get memory region\n");
-               ret = -ENODEV;
-               goto err;
-       }
-
-       ctrl->irq = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
-       if (ctrl->irq == NO_IRQ) {
-               dev_err(&ofdev->dev, "failed to get irq resource\n");
-               ret = -ENODEV;
-               goto err;
-       }
-
-       ctrl->dev = &ofdev->dev;
-
-       ret = fsl_elbc_ctrl_init(ctrl);
-       if (ret < 0)
-               goto err;
-
-       ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl);
-       if (ret != 0) {
-               dev_err(&ofdev->dev, "failed to install irq (%d)\n",
-                       ctrl->irq);
-               ret = ctrl->irq;
-               goto err;
-       }
-
-       for_each_child_of_node(ofdev->dev.of_node, child)
-               if (of_device_is_compatible(child, "fsl,elbc-fcm-nand"))
-                       fsl_elbc_chip_probe(ctrl, child);
+       mutex_unlock(&fsl_elbc_nand_mutex);
 
        return 0;
 
-err:
-       fsl_elbc_ctrl_remove(ofdev);
-       return ret;
 }
 
-static const struct of_device_id fsl_elbc_match[] = {
-       {
-               .compatible = "fsl,elbc",
-       },
+static const struct of_device_id fsl_elbc_nand_match[] = {
+       { .compatible = "fsl,elbc-fcm-nand", },
        {}
 };
 
-static struct of_platform_driver fsl_elbc_ctrl_driver = {
+static struct platform_driver fsl_elbc_nand_driver = {
        .driver = {
-               .name = "fsl-elbc",
+               .name = "fsl,elbc-fcm-nand",
                .owner = THIS_MODULE,
-               .of_match_table = fsl_elbc_match,
+               .of_match_table = fsl_elbc_nand_match,
        },
-       .probe = fsl_elbc_ctrl_probe,
-       .remove = fsl_elbc_ctrl_remove,
+       .probe = fsl_elbc_nand_probe,
+       .remove = fsl_elbc_nand_remove,
 };
 
-static int __init fsl_elbc_init(void)
+static int __init fsl_elbc_nand_init(void)
 {
-       return of_register_platform_driver(&fsl_elbc_ctrl_driver);
+       return platform_driver_register(&fsl_elbc_nand_driver);
 }
 
-static void __exit fsl_elbc_exit(void)
+static void __exit fsl_elbc_nand_exit(void)
 {
-       of_unregister_platform_driver(&fsl_elbc_ctrl_driver);
+       platform_driver_unregister(&fsl_elbc_nand_driver);
 }
 
-module_init(fsl_elbc_init);
-module_exit(fsl_elbc_exit);
+module_init(fsl_elbc_nand_init);
+module_exit(fsl_elbc_nand_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Freescale");
index 4eff8b2..efdcca9 100644 (file)
@@ -186,7 +186,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
        if (!flash_np)
                return -ENODEV;
 
-       fun->mtd.name = kasprintf(GFP_KERNEL, "%x.%s", io_res->start,
+       fun->mtd.name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start,
                                  flash_np->name);
        if (!fun->mtd.name) {
                ret = -ENOMEM;
@@ -222,7 +222,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
 {
        struct fsl_upm_nand *fun;
        struct resource io_res;
-       const uint32_t *prop;
+       const __be32 *prop;
        int rnb_gpio;
        int ret;
        int size;
@@ -270,7 +270,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
                        goto err1;
                }
                for (i = 0; i < fun->mchip_count; i++)
-                       fun->mchip_offsets[i] = prop[i];
+                       fun->mchip_offsets[i] = be32_to_cpu(prop[i]);
        } else {
                fun->mchip_count = 1;
        }
@@ -295,13 +295,13 @@ static int __devinit fun_probe(struct platform_device *ofdev,
 
        prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL);
        if (prop)
-               fun->chip_delay = *prop;
+               fun->chip_delay = be32_to_cpup(prop);
        else
                fun->chip_delay = 50;
 
        prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size);
        if (prop && size == sizeof(uint32_t))
-               fun->wait_flags = *prop;
+               fun->wait_flags = be32_to_cpup(prop);
        else
                fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
                                  FSL_UPM_WAIT_WRITE_BYTE;
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
new file mode 100644 (file)
index 0000000..02edfba
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+ * drivers/mtd/nand/fsmc_nand.c
+ *
+ * ST Microelectronics
+ * Flexible Static Memory Controller (FSMC)
+ * Driver for NAND portions
+ *
+ * Copyright Â© 2010 ST Microelectronics
+ * Vipin Kumar <vipin.kumar@st.com>
+ * Ashish Priyadarshi
+ *
+ * Based on drivers/mtd/nand/nomadik_nand.c
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/resource.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/mtd/fsmc.h>
+#include <mtd/mtd-abi.h>
+
+static struct nand_ecclayout fsmc_ecc1_layout = {
+       .eccbytes = 24,
+       .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
+               66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
+       .oobfree = {
+               {.offset = 8, .length = 8},
+               {.offset = 24, .length = 8},
+               {.offset = 40, .length = 8},
+               {.offset = 56, .length = 8},
+               {.offset = 72, .length = 8},
+               {.offset = 88, .length = 8},
+               {.offset = 104, .length = 8},
+               {.offset = 120, .length = 8}
+       }
+};
+
+static struct nand_ecclayout fsmc_ecc4_lp_layout = {
+       .eccbytes = 104,
+       .eccpos = {  2,   3,   4,   5,   6,   7,   8,
+               9,  10,  11,  12,  13,  14,
+               18,  19,  20,  21,  22,  23,  24,
+               25,  26,  27,  28,  29,  30,
+               34,  35,  36,  37,  38,  39,  40,
+               41,  42,  43,  44,  45,  46,
+               50,  51,  52,  53,  54,  55,  56,
+               57,  58,  59,  60,  61,  62,
+               66,  67,  68,  69,  70,  71,  72,
+               73,  74,  75,  76,  77,  78,
+               82,  83,  84,  85,  86,  87,  88,
+               89,  90,  91,  92,  93,  94,
+               98,  99, 100, 101, 102, 103, 104,
+               105, 106, 107, 108, 109, 110,
+               114, 115, 116, 117, 118, 119, 120,
+               121, 122, 123, 124, 125, 126
+       },
+       .oobfree = {
+               {.offset = 15, .length = 3},
+               {.offset = 31, .length = 3},
+               {.offset = 47, .length = 3},
+               {.offset = 63, .length = 3},
+               {.offset = 79, .length = 3},
+               {.offset = 95, .length = 3},
+               {.offset = 111, .length = 3},
+               {.offset = 127, .length = 1}
+       }
+};
+
+/*
+ * ECC placement definitions in oobfree type format.
+ * There are 13 bytes of ecc for every 512 byte block and it has to be read
+ * consecutively and immediately after the 512 byte data block for hardware to
+ * generate the error bit offsets in 512 byte data.
+ * Managing the ecc bytes in the following way makes it easier for software to
+ * read ecc bytes consecutive to data bytes. This way is similar to
+ * oobfree structure maintained already in generic nand driver
+ */
+static struct fsmc_eccplace fsmc_ecc4_lp_place = {
+       .eccplace = {
+               {.offset = 2, .length = 13},
+               {.offset = 18, .length = 13},
+               {.offset = 34, .length = 13},
+               {.offset = 50, .length = 13},
+               {.offset = 66, .length = 13},
+               {.offset = 82, .length = 13},
+               {.offset = 98, .length = 13},
+               {.offset = 114, .length = 13}
+       }
+};
+
+static struct nand_ecclayout fsmc_ecc4_sp_layout = {
+       .eccbytes = 13,
+       .eccpos = { 0,  1,  2,  3,  6,  7, 8,
+               9, 10, 11, 12, 13, 14
+       },
+       .oobfree = {
+               {.offset = 15, .length = 1},
+       }
+};
+
+static struct fsmc_eccplace fsmc_ecc4_sp_place = {
+       .eccplace = {
+               {.offset = 0, .length = 4},
+               {.offset = 6, .length = 9}
+       }
+};
+
+/*
+ * Default partition tables to be used if the partition information not
+ * provided through platform data
+ */
+#define PARTITION(n, off, sz)  {.name = n, .offset = off, .size = sz}
+
+/*
+ * Default partition layout for small page(= 512 bytes) devices
+ * Size for "Root file system" is updated in driver based on actual device size
+ */
+static struct mtd_partition partition_info_16KB_blk[] = {
+       PARTITION("X-loader", 0, 4 * 0x4000),
+       PARTITION("U-Boot", 0x10000, 20 * 0x4000),
+       PARTITION("Kernel", 0x60000, 256 * 0x4000),
+       PARTITION("Root File System", 0x460000, 0),
+};
+
+/*
+ * Default partition layout for large page(> 512 bytes) devices
+ * Size for "Root file system" is updated in driver based on actual device size
+ */
+static struct mtd_partition partition_info_128KB_blk[] = {
+       PARTITION("X-loader", 0, 4 * 0x20000),
+       PARTITION("U-Boot", 0x80000, 12 * 0x20000),
+       PARTITION("Kernel", 0x200000, 48 * 0x20000),
+       PARTITION("Root File System", 0x800000, 0),
+};
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+/**
+ * struct fsmc_nand_data - atructure for FSMC NAND device state
+ *
+ * @mtd:               MTD info for a NAND flash.
+ * @nand:              Chip related info for a NAND flash.
+ * @partitions:                Partition info for a NAND Flash.
+ * @nr_partitions:     Total number of partition of a NAND flash.
+ *
+ * @ecc_place:         ECC placing locations in oobfree type format.
+ * @bank:              Bank number for probed device.
+ * @clk:               Clock structure for FSMC.
+ *
+ * @data_va:           NAND port for Data.
+ * @cmd_va:            NAND port for Command.
+ * @addr_va:           NAND port for Address.
+ * @regs_va:           FSMC regs base address.
+ */
+struct fsmc_nand_data {
+       struct mtd_info         mtd;
+       struct nand_chip        nand;
+       struct mtd_partition    *partitions;
+       unsigned int            nr_partitions;
+
+       struct fsmc_eccplace    *ecc_place;
+       unsigned int            bank;
+       struct clk              *clk;
+
+       struct resource         *resregs;
+       struct resource         *rescmd;
+       struct resource         *resaddr;
+       struct resource         *resdata;
+
+       void __iomem            *data_va;
+       void __iomem            *cmd_va;
+       void __iomem            *addr_va;
+       void __iomem            *regs_va;
+
+       void                    (*select_chip)(uint32_t bank, uint32_t busw);
+};
+
+/* Assert CS signal based on chipnr */
+static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct fsmc_nand_data *host;
+
+       host = container_of(mtd, struct fsmc_nand_data, mtd);
+
+       switch (chipnr) {
+       case -1:
+               chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+               break;
+       case 0:
+       case 1:
+       case 2:
+       case 3:
+               if (host->select_chip)
+                       host->select_chip(chipnr,
+                                       chip->options & NAND_BUSWIDTH_16);
+               break;
+
+       default:
+               BUG();
+       }
+}
+
+/*
+ * fsmc_cmd_ctrl - For facilitaing Hardware access
+ * This routine allows hardware specific access to control-lines(ALE,CLE)
+ */
+static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+       struct nand_chip *this = mtd->priv;
+       struct fsmc_nand_data *host = container_of(mtd,
+                                       struct fsmc_nand_data, mtd);
+       struct fsmc_regs *regs = host->regs_va;
+       unsigned int bank = host->bank;
+
+       if (ctrl & NAND_CTRL_CHANGE) {
+               if (ctrl & NAND_CLE) {
+                       this->IO_ADDR_R = (void __iomem *)host->cmd_va;
+                       this->IO_ADDR_W = (void __iomem *)host->cmd_va;
+               } else if (ctrl & NAND_ALE) {
+                       this->IO_ADDR_R = (void __iomem *)host->addr_va;
+                       this->IO_ADDR_W = (void __iomem *)host->addr_va;
+               } else {
+                       this->IO_ADDR_R = (void __iomem *)host->data_va;
+                       this->IO_ADDR_W = (void __iomem *)host->data_va;
+               }
+
+               if (ctrl & NAND_NCE) {
+                       writel(readl(&regs->bank_regs[bank].pc) | FSMC_ENABLE,
+                                       &regs->bank_regs[bank].pc);
+               } else {
+                       writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ENABLE,
+                                      &regs->bank_regs[bank].pc);
+               }
+       }
+
+       mb();
+
+       if (cmd != NAND_CMD_NONE)
+               writeb(cmd, this->IO_ADDR_W);
+}
+
+/*
+ * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine
+ *
+ * This routine initializes timing parameters related to NAND memory access in
+ * FSMC registers
+ */
+static void __init fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank,
+                                  uint32_t busw)
+{
+       uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
+
+       if (busw)
+               writel(value | FSMC_DEVWID_16, &regs->bank_regs[bank].pc);
+       else
+               writel(value | FSMC_DEVWID_8, &regs->bank_regs[bank].pc);
+
+       writel(readl(&regs->bank_regs[bank].pc) | FSMC_TCLR_1 | FSMC_TAR_1,
+              &regs->bank_regs[bank].pc);
+       writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
+              &regs->bank_regs[bank].comm);
+       writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
+              &regs->bank_regs[bank].attrib);
+}
+
+/*
+ * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers
+ */
+static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       struct fsmc_nand_data *host = container_of(mtd,
+                                       struct fsmc_nand_data, mtd);
+       struct fsmc_regs *regs = host->regs_va;
+       uint32_t bank = host->bank;
+
+       writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ECCPLEN_256,
+                      &regs->bank_regs[bank].pc);
+       writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ECCEN,
+                       &regs->bank_regs[bank].pc);
+       writel(readl(&regs->bank_regs[bank].pc) | FSMC_ECCEN,
+                       &regs->bank_regs[bank].pc);
+}
+
+/*
+ * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by
+ * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction upto
+ * max of 8-bits)
+ */
+static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
+                               uint8_t *ecc)
+{
+       struct fsmc_nand_data *host = container_of(mtd,
+                                       struct fsmc_nand_data, mtd);
+       struct fsmc_regs *regs = host->regs_va;
+       uint32_t bank = host->bank;
+       uint32_t ecc_tmp;
+       unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
+
+       do {
+               if (readl(&regs->bank_regs[bank].sts) & FSMC_CODE_RDY)
+                       break;
+               else
+                       cond_resched();
+       } while (!time_after_eq(jiffies, deadline));
+
+       ecc_tmp = readl(&regs->bank_regs[bank].ecc1);
+       ecc[0] = (uint8_t) (ecc_tmp >> 0);
+       ecc[1] = (uint8_t) (ecc_tmp >> 8);
+       ecc[2] = (uint8_t) (ecc_tmp >> 16);
+       ecc[3] = (uint8_t) (ecc_tmp >> 24);
+
+       ecc_tmp = readl(&regs->bank_regs[bank].ecc2);
+       ecc[4] = (uint8_t) (ecc_tmp >> 0);
+       ecc[5] = (uint8_t) (ecc_tmp >> 8);
+       ecc[6] = (uint8_t) (ecc_tmp >> 16);
+       ecc[7] = (uint8_t) (ecc_tmp >> 24);
+
+       ecc_tmp = readl(&regs->bank_regs[bank].ecc3);
+       ecc[8] = (uint8_t) (ecc_tmp >> 0);
+       ecc[9] = (uint8_t) (ecc_tmp >> 8);
+       ecc[10] = (uint8_t) (ecc_tmp >> 16);
+       ecc[11] = (uint8_t) (ecc_tmp >> 24);
+
+       ecc_tmp = readl(&regs->bank_regs[bank].sts);
+       ecc[12] = (uint8_t) (ecc_tmp >> 16);
+
+       return 0;
+}
+
+/*
+ * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by
+ * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction upto
+ * max of 1-bit)
+ */
+static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
+                               uint8_t *ecc)
+{
+       struct fsmc_nand_data *host = container_of(mtd,
+                                       struct fsmc_nand_data, mtd);
+       struct fsmc_regs *regs = host->regs_va;
+       uint32_t bank = host->bank;
+       uint32_t ecc_tmp;
+
+       ecc_tmp = readl(&regs->bank_regs[bank].ecc1);
+       ecc[0] = (uint8_t) (ecc_tmp >> 0);
+       ecc[1] = (uint8_t) (ecc_tmp >> 8);
+       ecc[2] = (uint8_t) (ecc_tmp >> 16);
+
+       return 0;
+}
+
+/*
+ * fsmc_read_page_hwecc
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       buffer to store read data
+ * @page:      page number to read
+ *
+ * This routine is needed for fsmc verison 8 as reading from NAND chip has to be
+ * performed in a strict sequence as follows:
+ * data(512 byte) -> ecc(13 byte)
+ * After this read, fsmc hardware generates and reports error data bits(upto a
+ * max of 8 bits)
+ */
+static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+                                uint8_t *buf, int page)
+{
+       struct fsmc_nand_data *host = container_of(mtd,
+                                       struct fsmc_nand_data, mtd);
+       struct fsmc_eccplace *ecc_place = host->ecc_place;
+       int i, j, s, stat, eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       int eccsteps = chip->ecc.steps;
+       uint8_t *p = buf;
+       uint8_t *ecc_calc = chip->buffers->ecccalc;
+       uint8_t *ecc_code = chip->buffers->ecccode;
+       int off, len, group = 0;
+       /*
+        * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we
+        * end up reading 14 bytes (7 words) from oob. The local array is
+        * to maintain word alignment
+        */
+       uint16_t ecc_oob[7];
+       uint8_t *oob = (uint8_t *)&ecc_oob[0];
+
+       for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
+
+               chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page);
+               chip->ecc.hwctl(mtd, NAND_ECC_READ);
+               chip->read_buf(mtd, p, eccsize);
+
+               for (j = 0; j < eccbytes;) {
+                       off = ecc_place->eccplace[group].offset;
+                       len = ecc_place->eccplace[group].length;
+                       group++;
+
+                       /*
+                       * length is intentionally kept a higher multiple of 2
+                       * to read at least 13 bytes even in case of 16 bit NAND
+                       * devices
+                       */
+                       len = roundup(len, 2);
+                       chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page);
+                       chip->read_buf(mtd, oob + j, len);
+                       j += len;
+               }
+
+               memcpy(&ecc_code[i], oob, 13);
+               chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+               stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+               if (stat < 0)
+                       mtd->ecc_stats.failed++;
+               else
+                       mtd->ecc_stats.corrected += stat;
+       }
+
+       return 0;
+}
+
+/*
+ * fsmc_correct_data
+ * @mtd:       mtd info structure
+ * @dat:       buffer of read data
+ * @read_ecc:  ecc read from device spare area
+ * @calc_ecc:  ecc calculated from read data
+ *
+ * calc_ecc is a 104 bit information containing maximum of 8 error
+ * offset informations of 13 bits each in 512 bytes of read data.
+ */
+static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
+                            uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+       struct fsmc_nand_data *host = container_of(mtd,
+                                       struct fsmc_nand_data, mtd);
+       struct fsmc_regs *regs = host->regs_va;
+       unsigned int bank = host->bank;
+       uint16_t err_idx[8];
+       uint64_t ecc_data[2];
+       uint32_t num_err, i;
+
+       /* The calculated ecc is actually the correction index in data */
+       memcpy(ecc_data, calc_ecc, 13);
+
+       /*
+        * ------------------- calc_ecc[] bit wise -----------|--13 bits--|
+        * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--|
+        *
+        * calc_ecc is a 104 bit information containing maximum of 8 error
+        * offset informations of 13 bits each. calc_ecc is copied into a
+        * uint64_t array and error offset indexes are populated in err_idx
+        * array
+        */
+       for (i = 0; i < 8; i++) {
+               if (i == 4) {
+                       err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0];
+                       ecc_data[1] >>= 1;
+                       continue;
+               }
+               err_idx[i] = (ecc_data[i/4] & 0x1FFF);
+               ecc_data[i/4] >>= 13;
+       }
+
+       num_err = (readl(&regs->bank_regs[bank].sts) >> 10) & 0xF;
+
+       if (num_err == 0xF)
+               return -EBADMSG;
+
+       i = 0;
+       while (num_err--) {
+               change_bit(0, (unsigned long *)&err_idx[i]);
+               change_bit(1, (unsigned long *)&err_idx[i]);
+
+               if (err_idx[i] <= 512 * 8) {
+                       change_bit(err_idx[i], (unsigned long *)dat);
+                       i++;
+               }
+       }
+       return i;
+}
+
+/*
+ * fsmc_nand_probe - Probe function
+ * @pdev:       platform device structure
+ */
+static int __init fsmc_nand_probe(struct platform_device *pdev)
+{
+       struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct fsmc_nand_data *host;
+       struct mtd_info *mtd;
+       struct nand_chip *nand;
+       struct fsmc_regs *regs;
+       struct resource *res;
+       int nr_parts, ret = 0;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "platform data is NULL\n");
+               return -EINVAL;
+       }
+
+       /* Allocate memory for the device structure (and zero it) */
+       host = kzalloc(sizeof(*host), GFP_KERNEL);
+       if (!host) {
+               dev_err(&pdev->dev, "failed to allocate device structure\n");
+               return -ENOMEM;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
+       if (!res) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->resdata = request_mem_region(res->start, resource_size(res),
+                       pdev->name);
+       if (!host->resdata) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->data_va = ioremap(res->start, resource_size(res));
+       if (!host->data_va) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->resaddr = request_mem_region(res->start + PLAT_NAND_ALE,
+                       resource_size(res), pdev->name);
+       if (!host->resaddr) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->addr_va = ioremap(res->start + PLAT_NAND_ALE, resource_size(res));
+       if (!host->addr_va) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->rescmd = request_mem_region(res->start + PLAT_NAND_CLE,
+                       resource_size(res), pdev->name);
+       if (!host->rescmd) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->cmd_va = ioremap(res->start + PLAT_NAND_CLE, resource_size(res));
+       if (!host->cmd_va) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
+       if (!res) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->resregs = request_mem_region(res->start, resource_size(res),
+                       pdev->name);
+       if (!host->resregs) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->regs_va = ioremap(res->start, resource_size(res));
+       if (!host->regs_va) {
+               ret = -EIO;
+               goto err_probe1;
+       }
+
+       host->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(host->clk)) {
+               dev_err(&pdev->dev, "failed to fetch block clock\n");
+               ret = PTR_ERR(host->clk);
+               host->clk = NULL;
+               goto err_probe1;
+       }
+
+       ret = clk_enable(host->clk);
+       if (ret)
+               goto err_probe1;
+
+       host->bank = pdata->bank;
+       host->select_chip = pdata->select_bank;
+       regs = host->regs_va;
+
+       /* Link all private pointers */
+       mtd = &host->mtd;
+       nand = &host->nand;
+       mtd->priv = nand;
+       nand->priv = host;
+
+       host->mtd.owner = THIS_MODULE;
+       nand->IO_ADDR_R = host->data_va;
+       nand->IO_ADDR_W = host->data_va;
+       nand->cmd_ctrl = fsmc_cmd_ctrl;
+       nand->chip_delay = 30;
+
+       nand->ecc.mode = NAND_ECC_HW;
+       nand->ecc.hwctl = fsmc_enable_hwecc;
+       nand->ecc.size = 512;
+       nand->options = pdata->options;
+       nand->select_chip = fsmc_select_chip;
+
+       if (pdata->width == FSMC_NAND_BW16)
+               nand->options |= NAND_BUSWIDTH_16;
+
+       fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16);
+
+       if (get_fsmc_version(host->regs_va) == FSMC_VER8) {
+               nand->ecc.read_page = fsmc_read_page_hwecc;
+               nand->ecc.calculate = fsmc_read_hwecc_ecc4;
+               nand->ecc.correct = fsmc_correct_data;
+               nand->ecc.bytes = 13;
+       } else {
+               nand->ecc.calculate = fsmc_read_hwecc_ecc1;
+               nand->ecc.correct = nand_correct_data;
+               nand->ecc.bytes = 3;
+       }
+
+       /*
+        * Scan to find existance of the device
+        */
+       if (nand_scan_ident(&host->mtd, 1, NULL)) {
+               ret = -ENXIO;
+               dev_err(&pdev->dev, "No NAND Device found!\n");
+               goto err_probe;
+       }
+
+       if (get_fsmc_version(host->regs_va) == FSMC_VER8) {
+               if (host->mtd.writesize == 512) {
+                       nand->ecc.layout = &fsmc_ecc4_sp_layout;
+                       host->ecc_place = &fsmc_ecc4_sp_place;
+               } else {
+                       nand->ecc.layout = &fsmc_ecc4_lp_layout;
+                       host->ecc_place = &fsmc_ecc4_lp_place;
+               }
+       } else {
+               nand->ecc.layout = &fsmc_ecc1_layout;
+       }
+
+       /* Second stage of scan to fill MTD data-structures */
+       if (nand_scan_tail(&host->mtd)) {
+               ret = -ENXIO;
+               goto err_probe;
+       }
+
+       /*
+        * The partition information can is accessed by (in the same precedence)
+        *
+        * command line through Bootloader,
+        * platform data,
+        * default partition information present in driver.
+        */
+#ifdef CONFIG_MTD_PARTITIONS
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+       /*
+        * Check if partition info passed via command line
+        */
+       host->mtd.name = "nand";
+       nr_parts = parse_mtd_partitions(&host->mtd, part_probes,
+                       &host->partitions, 0);
+       if (nr_parts > 0) {
+               host->nr_partitions = nr_parts;
+       } else {
+#endif
+               /*
+                * Check if partition info passed via command line
+                */
+               if (pdata->partitions) {
+                       host->partitions = pdata->partitions;
+                       host->nr_partitions = pdata->nr_partitions;
+               } else {
+                       struct mtd_partition *partition;
+                       int i;
+
+                       /* Select the default partitions info */
+                       switch (host->mtd.size) {
+                       case 0x01000000:
+                       case 0x02000000:
+                       case 0x04000000:
+                               host->partitions = partition_info_16KB_blk;
+                               host->nr_partitions =
+                                       sizeof(partition_info_16KB_blk) /
+                                       sizeof(struct mtd_partition);
+                               break;
+                       case 0x08000000:
+                       case 0x10000000:
+                       case 0x20000000:
+                       case 0x40000000:
+                               host->partitions = partition_info_128KB_blk;
+                               host->nr_partitions =
+                                       sizeof(partition_info_128KB_blk) /
+                                       sizeof(struct mtd_partition);
+                               break;
+                       default:
+                               ret = -ENXIO;
+                               pr_err("Unsupported NAND size\n");
+                               goto err_probe;
+                       }
+
+                       partition = host->partitions;
+                       for (i = 0; i < host->nr_partitions; i++, partition++) {
+                               if (partition->size == 0) {
+                                       partition->size = host->mtd.size -
+                                               partition->offset;
+                                       break;
+                               }
+                       }
+               }
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+       }
+#endif
+
+       if (host->partitions) {
+               ret = add_mtd_partitions(&host->mtd, host->partitions,
+                               host->nr_partitions);
+               if (ret)
+                       goto err_probe;
+       }
+#else
+       dev_info(&pdev->dev, "Registering %s as whole device\n", mtd->name);
+       if (!add_mtd_device(mtd)) {
+               ret = -ENXIO;
+               goto err_probe;
+       }
+#endif
+
+       platform_set_drvdata(pdev, host);
+       dev_info(&pdev->dev, "FSMC NAND driver registration successful\n");
+       return 0;
+
+err_probe:
+       clk_disable(host->clk);
+err_probe1:
+       if (host->clk)
+               clk_put(host->clk);
+       if (host->regs_va)
+               iounmap(host->regs_va);
+       if (host->resregs)
+               release_mem_region(host->resregs->start,
+                               resource_size(host->resregs));
+       if (host->cmd_va)
+               iounmap(host->cmd_va);
+       if (host->rescmd)
+               release_mem_region(host->rescmd->start,
+                               resource_size(host->rescmd));
+       if (host->addr_va)
+               iounmap(host->addr_va);
+       if (host->resaddr)
+               release_mem_region(host->resaddr->start,
+                               resource_size(host->resaddr));
+       if (host->data_va)
+               iounmap(host->data_va);
+       if (host->resdata)
+               release_mem_region(host->resdata->start,
+                               resource_size(host->resdata));
+
+       kfree(host);
+       return ret;
+}
+
+/*
+ * Clean up routine
+ */
+static int fsmc_nand_remove(struct platform_device *pdev)
+{
+       struct fsmc_nand_data *host = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       if (host) {
+#ifdef CONFIG_MTD_PARTITIONS
+               del_mtd_partitions(&host->mtd);
+#else
+               del_mtd_device(&host->mtd);
+#endif
+               clk_disable(host->clk);
+               clk_put(host->clk);
+
+               iounmap(host->regs_va);
+               release_mem_region(host->resregs->start,
+                               resource_size(host->resregs));
+               iounmap(host->cmd_va);
+               release_mem_region(host->rescmd->start,
+                               resource_size(host->rescmd));
+               iounmap(host->addr_va);
+               release_mem_region(host->resaddr->start,
+                               resource_size(host->resaddr));
+               iounmap(host->data_va);
+               release_mem_region(host->resdata->start,
+                               resource_size(host->resdata));
+
+               kfree(host);
+       }
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int fsmc_nand_suspend(struct device *dev)
+{
+       struct fsmc_nand_data *host = dev_get_drvdata(dev);
+       if (host)
+               clk_disable(host->clk);
+       return 0;
+}
+
+static int fsmc_nand_resume(struct device *dev)
+{
+       struct fsmc_nand_data *host = dev_get_drvdata(dev);
+       if (host)
+               clk_enable(host->clk);
+       return 0;
+}
+
+static const struct dev_pm_ops fsmc_nand_pm_ops = {
+       .suspend = fsmc_nand_suspend,
+       .resume = fsmc_nand_resume,
+};
+#endif
+
+static struct platform_driver fsmc_nand_driver = {
+       .remove = fsmc_nand_remove,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "fsmc-nand",
+#ifdef CONFIG_PM
+               .pm = &fsmc_nand_pm_ops,
+#endif
+       },
+};
+
+static int __init fsmc_nand_init(void)
+{
+       return platform_driver_probe(&fsmc_nand_driver,
+                                    fsmc_nand_probe);
+}
+module_init(fsmc_nand_init);
+
+static void __exit fsmc_nand_exit(void)
+{
+       platform_driver_unregister(&fsmc_nand_driver);
+}
+module_exit(fsmc_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi");
+MODULE_DESCRIPTION("NAND driver for SPEAr Platforms");
index df0c1da..469e649 100644 (file)
@@ -568,6 +568,7 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
        uint rcw_width;
        uint rcwh;
        uint romloc, ps;
+       int ret = 0;
 
        rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset");
        if (!rmnode) {
@@ -579,7 +580,8 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
        rm = of_iomap(rmnode, 0);
        if (!rm) {
                dev_err(prv->dev, "Error mapping reset module node!\n");
-               return -EBUSY;
+               ret = -EBUSY;
+               goto out;
        }
 
        rcwh = in_be32(&rm->rcwhr);
@@ -628,8 +630,9 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
                                rcw_width * 8, rcw_pagesize,
                                rcw_sparesize);
        iounmap(rm);
+out:
        of_node_put(rmnode);
-       return 0;
+       return ret;
 }
 
 /* Free driver resources */
@@ -660,7 +663,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
 #endif
        struct nand_chip *chip;
        unsigned long regs_paddr, regs_size;
-       const uint *chips_no;
+       const __be32 *chips_no;
        int resettime = 0;
        int retval = 0;
        int rev, len;
@@ -803,7 +806,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
        }
 
        /* Detect NAND chips */
-       if (nand_scan(mtd, *chips_no)) {
+       if (nand_scan(mtd, be32_to_cpup(chips_no))) {
                dev_err(dev, "NAND Flash not found !\n");
                devm_free_irq(dev, prv->irq, mtd);
                retval = -ENXIO;
index d551ddd..1f75a1b 100644 (file)
@@ -45,7 +45,7 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/leds.h>
-#include <asm/io.h>
+#include <linux/io.h>
 
 #ifdef CONFIG_MTD_PARTITIONS
 #include <linux/mtd/partitions.h>
@@ -59,7 +59,7 @@ static struct nand_ecclayout nand_oob_8 = {
                {.offset = 3,
                 .length = 2},
                {.offset = 6,
-                .length = 2}}
+                .length = 2} }
 };
 
 static struct nand_ecclayout nand_oob_16 = {
@@ -67,7 +67,7 @@ static struct nand_ecclayout nand_oob_16 = {
        .eccpos = {0, 1, 2, 3, 6, 7},
        .oobfree = {
                {.offset = 8,
-                . length = 8}}
+                . length = 8} }
 };
 
 static struct nand_ecclayout nand_oob_64 = {
@@ -78,7 +78,7 @@ static struct nand_ecclayout nand_oob_64 = {
                   56, 57, 58, 59, 60, 61, 62, 63},
        .oobfree = {
                {.offset = 2,
-                .length = 38}}
+                .length = 38} }
 };
 
 static struct nand_ecclayout nand_oob_128 = {
@@ -92,7 +92,7 @@ static struct nand_ecclayout nand_oob_128 = {
                   120, 121, 122, 123, 124, 125, 126, 127},
        .oobfree = {
                {.offset = 2,
-                .length = 78}}
+                .length = 78} }
 };
 
 static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
@@ -612,7 +612,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
                               NAND_CTRL_CLE | NAND_CTRL_CHANGE);
                chip->cmd_ctrl(mtd,
                               NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
-               while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
+               while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
+                               ;
                return;
 
                /* This applies to read commands */
@@ -718,7 +719,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
                               NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
                chip->cmd_ctrl(mtd, NAND_CMD_NONE,
                               NAND_NCE | NAND_CTRL_CHANGE);
-               while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
+               while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
+                               ;
                return;
 
        case NAND_CMD_RNDOUT:
@@ -784,7 +786,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
        spinlock_t *lock = &chip->controller->lock;
        wait_queue_head_t *wq = &chip->controller->wq;
        DECLARE_WAITQUEUE(wait, current);
- retry:
+retry:
        spin_lock(lock);
 
        /* Hardware controller shared among independent devices */
@@ -834,7 +836,7 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
                                break;
                }
                mdelay(1);
-        }
+       }
 }
 
 /**
@@ -980,6 +982,7 @@ out:
 
        return ret;
 }
+EXPORT_SYMBOL(nand_unlock);
 
 /**
  * nand_lock - [REPLACEABLE] locks all blocks present in the device
@@ -1049,6 +1052,7 @@ out:
 
        return ret;
 }
+EXPORT_SYMBOL(nand_lock);
 
 /**
  * nand_read_page_raw - [Intern] read raw page data without ecc
@@ -1076,8 +1080,9 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
  *
  * We need a special oob layout and handling even when OOB isn't used.
  */
-static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
-                             uint8_t *buf, int page)
+static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
+                                       struct nand_chip *chip,
+                                       uint8_t *buf, int page)
 {
        int eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -1158,7 +1163,8 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
  * @readlen:   data length
  * @bufpoi:    buffer to store read data
  */
-static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+                       uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
 {
        int start_step, end_step, num_steps;
        uint32_t *eccpos = chip->ecc.layout->eccpos;
@@ -1166,6 +1172,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3
        int data_col_addr, i, gaps = 0;
        int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
        int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+       int index = 0;
 
        /* Column address wihin the page aligned to ECC size (256bytes). */
        start_step = data_offs / chip->ecc.size;
@@ -1204,26 +1211,30 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3
        } else {
                /* send the command to read the particular ecc bytes */
                /* take care about buswidth alignment in read_buf */
-               aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
+               index = start_step * chip->ecc.bytes;
+
+               aligned_pos = eccpos[index] & ~(busw - 1);
                aligned_len = eccfrag_len;
-               if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
+               if (eccpos[index] & (busw - 1))
                        aligned_len++;
-               if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
+               if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
                        aligned_len++;
 
-               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+                                       mtd->writesize + aligned_pos, -1);
                chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
        }
 
        for (i = 0; i < eccfrag_len; i++)
-               chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
+               chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
 
        p = bufpoi + data_col_addr;
        for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
                int stat;
 
-               stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
-               if (stat == -1)
+               stat = chip->ecc.correct(mtd, p,
+                       &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
+               if (stat < 0)
                        mtd->ecc_stats.failed++;
                else
                        mtd->ecc_stats.corrected += stat;
@@ -1390,7 +1401,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
 static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
                                  struct mtd_oob_ops *ops, size_t len)
 {
-       switch(ops->mode) {
+       switch (ops->mode) {
 
        case MTD_OOB_PLACE:
        case MTD_OOB_RAW:
@@ -1402,7 +1413,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
                uint32_t boffs = 0, roffs = ops->ooboffs;
                size_t bytes = 0;
 
-               for(; free->length && len; free++, len -= bytes) {
+               for (; free->length && len; free++, len -= bytes) {
                        /* Read request not from offset 0 ? */
                        if (unlikely(roffs)) {
                                if (roffs >= free->length) {
@@ -1466,7 +1477,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
        buf = ops->datbuf;
        oob = ops->oobbuf;
 
-       while(1) {
+       while (1) {
                bytes = min(mtd->writesize - col, readlen);
                aligned = (bytes == mtd->writesize);
 
@@ -1484,7 +1495,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                                ret = chip->ecc.read_page_raw(mtd, chip,
                                                              bufpoi, page);
                        else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
-                               ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
+                               ret = chip->ecc.read_subpage(mtd, chip,
+                                                       col, bytes, bufpoi);
                        else
                                ret = chip->ecc.read_page(mtd, chip, bufpoi,
                                                          page);
@@ -1493,7 +1505,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 
                        /* Transfer not aligned data */
                        if (!aligned) {
-                               if (!NAND_SUBPAGE_READ(chip) && !oob)
+                               if (!NAND_SUBPAGE_READ(chip) && !oob &&
+                                   !(mtd->ecc_stats.failed - stats.failed))
                                        chip->pagebuf = realpage;
                                memcpy(buf, chip->buffers->databuf + col, bytes);
                        }
@@ -1791,7 +1804,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
        realpage = (int)(from >> chip->page_shift);
        page = realpage & chip->pagemask;
 
-       while(1) {
+       while (1) {
                sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
 
                len = min(len, readlen);
@@ -1861,7 +1874,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
 
        nand_get_device(chip, mtd, FL_READING);
 
-       switch(ops->mode) {
+       switch (ops->mode) {
        case MTD_OOB_PLACE:
        case MTD_OOB_AUTO:
        case MTD_OOB_RAW:
@@ -1876,7 +1889,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
        else
                ret = nand_do_read_ops(mtd, from, ops);
 
- out:
+out:
        nand_release_device(mtd);
        return ret;
 }
@@ -1905,8 +1918,9 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
  *
  * We need a special oob layout and handling even when ECC isn't checked.
  */
-static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
-                               const uint8_t *buf)
+static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
+                                       struct nand_chip *chip,
+                                       const uint8_t *buf)
 {
        int eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -2099,7 +2113,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
 static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
                                                struct mtd_oob_ops *ops)
 {
-       switch(ops->mode) {
+       switch (ops->mode) {
 
        case MTD_OOB_PLACE:
        case MTD_OOB_RAW:
@@ -2111,7 +2125,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
                uint32_t boffs = 0, woffs = ops->ooboffs;
                size_t bytes = 0;
 
-               for(; free->length && len; free++, len -= bytes) {
+               for (; free->length && len; free++, len -= bytes) {
                        /* Write request not from offset 0 ? */
                        if (unlikely(woffs)) {
                                if (woffs >= free->length) {
@@ -2137,7 +2151,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
        return NULL;
 }
 
-#define NOTALIGNED(x)  (x & (chip->subpagesize - 1)) != 0
+#define NOTALIGNED(x)  ((x & (chip->subpagesize - 1)) != 0)
 
 /**
  * nand_do_write_ops - [Internal] NAND write with ECC
@@ -2200,10 +2214,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                memset(chip->oob_poi, 0xff, mtd->oobsize);
 
        /* Don't allow multipage oob writes with offset */
-       if (ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
+       if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
                return -EINVAL;
 
-       while(1) {
+       while (1) {
                int bytes = mtd->writesize;
                int cached = writelen > bytes && page != blockmask;
                uint8_t *wbuf = buf;
@@ -2431,7 +2445,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
 
        nand_get_device(chip, mtd, FL_WRITING);
 
-       switch(ops->mode) {
+       switch (ops->mode) {
        case MTD_OOB_PLACE:
        case MTD_OOB_AUTO:
        case MTD_OOB_RAW:
@@ -2446,7 +2460,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
        else
                ret = nand_do_write_ops(mtd, to, ops);
 
- out:
+out:
        nand_release_device(mtd);
        return ret;
 }
@@ -2511,7 +2525,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
 {
        int page, status, pages_per_block, ret, chipnr;
        struct nand_chip *chip = mtd->priv;
-       loff_t rewrite_bbt[NAND_MAX_CHIPS]={0};
+       loff_t rewrite_bbt[NAND_MAX_CHIPS] = {0};
        unsigned int bbt_masked_page = 0xffffffff;
        loff_t len;
 
@@ -2632,7 +2646,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
        }
        instr->state = MTD_ERASE_DONE;
 
- erase_exit:
+erase_exit:
 
        ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
 
@@ -2706,7 +2720,8 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
        struct nand_chip *chip = mtd->priv;
        int ret;
 
-       if ((ret = nand_block_isbad(mtd, ofs))) {
+       ret = nand_block_isbad(mtd, ofs);
+       if (ret) {
                /* If it was bad already, return success and do nothing. */
                if (ret > 0)
                        return 0;
@@ -2786,16 +2801,116 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
 
 }
 
+/*
+ * sanitize ONFI strings so we can safely print them
+ */
+static void sanitize_string(uint8_t *s, size_t len)
+{
+       ssize_t i;
+
+       /* null terminate */
+       s[len - 1] = 0;
+
+       /* remove non printable chars */
+       for (i = 0; i < len - 1; i++) {
+               if (s[i] < ' ' || s[i] > 127)
+                       s[i] = '?';
+       }
+
+       /* remove trailing spaces */
+       strim(s);
+}
+
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+       int i;
+       while (len--) {
+               crc ^= *p++ << 8;
+               for (i = 0; i < 8; i++)
+                       crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+       }
+
+       return crc;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ */
+static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
+                                       int busw)
+{
+       struct nand_onfi_params *p = &chip->onfi_params;
+       int i;
+       int val;
+
+       /* try ONFI for unknow chip or LP */
+       chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+       if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+               chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+               return 0;
+
+       printk(KERN_INFO "ONFI flash detected\n");
+       chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+       for (i = 0; i < 3; i++) {
+               chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+               if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+                               le16_to_cpu(p->crc)) {
+                       printk(KERN_INFO "ONFI param page %d valid\n", i);
+                       break;
+               }
+       }
+
+       if (i == 3)
+               return 0;
+
+       /* check version */
+       val = le16_to_cpu(p->revision);
+       if (val == 1 || val > (1 << 4)) {
+               printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
+                                                               __func__, val);
+               return 0;
+       }
+
+       if (val & (1 << 4))
+               chip->onfi_version = 22;
+       else if (val & (1 << 3))
+               chip->onfi_version = 21;
+       else if (val & (1 << 2))
+               chip->onfi_version = 20;
+       else
+               chip->onfi_version = 10;
+
+       sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+       sanitize_string(p->model, sizeof(p->model));
+       if (!mtd->name)
+               mtd->name = p->model;
+       mtd->writesize = le32_to_cpu(p->byte_per_page);
+       mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+       mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+       chip->chipsize = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+       busw = 0;
+       if (le16_to_cpu(p->features) & 1)
+               busw = NAND_BUSWIDTH_16;
+
+       chip->options &= ~NAND_CHIPOPTIONS_MSK;
+       chip->options |= (NAND_NO_READRDY |
+                       NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
+
+       return 1;
+}
+
 /*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
 static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                                                  struct nand_chip *chip,
-                                                 int busw, int *maf_id,
+                                                 int busw,
+                                                 int *maf_id, int *dev_id,
                                                  struct nand_flash_dev *type)
 {
-       int i, dev_id, maf_idx;
+       int i, maf_idx;
        u8 id_data[8];
+       int ret;
 
        /* Select the device */
        chip->select_chip(mtd, 0);
@@ -2811,7 +2926,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 
        /* Read manufacturer and device IDs */
        *maf_id = chip->read_byte(mtd);
-       dev_id = chip->read_byte(mtd);
+       *dev_id = chip->read_byte(mtd);
 
        /* Try again to make sure, as some systems the bus-hold or other
         * interface concerns can cause random data which looks like a
@@ -2821,15 +2936,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 
        chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 
-       /* Read entire ID string */
-
-       for (i = 0; i < 8; i++)
+       for (i = 0; i < 2; i++)
                id_data[i] = chip->read_byte(mtd);
 
-       if (id_data[0] != *maf_id || id_data[1] != dev_id) {
+       if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
                printk(KERN_INFO "%s: second ID read did not match "
                       "%02x,%02x against %02x,%02x\n", __func__,
-                      *maf_id, dev_id, id_data[0], id_data[1]);
+                      *maf_id, *dev_id, id_data[0], id_data[1]);
                return ERR_PTR(-ENODEV);
        }
 
@@ -2837,8 +2950,23 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                type = nand_flash_ids;
 
        for (; type->name != NULL; type++)
-               if (dev_id == type->id)
-                        break;
+               if (*dev_id == type->id)
+                       break;
+
+       chip->onfi_version = 0;
+       if (!type->name || !type->pagesize) {
+               /* Check is chip is ONFI compliant */
+               ret = nand_flash_detect_onfi(mtd, chip, busw);
+               if (ret)
+                       goto ident_done;
+       }
+
+       chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+       /* Read entire ID string */
+
+       for (i = 0; i < 8; i++)
+               id_data[i] = chip->read_byte(mtd);
 
        if (!type->name)
                return ERR_PTR(-ENODEV);
@@ -2848,8 +2976,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 
        chip->chipsize = (uint64_t)type->chipsize << 20;
 
-       /* Newer devices have all the information in additional id bytes */
-       if (!type->pagesize) {
+       if (!type->pagesize && chip->init_size) {
+               /* set the pagesize, oobsize, erasesize by the driver*/
+               busw = chip->init_size(mtd, chip, id_data);
+       } else if (!type->pagesize) {
                int extid;
                /* The 3rd id byte holds MLC / multichip data */
                chip->cellinfo = id_data[2];
@@ -2859,7 +2989,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                /*
                 * Field definitions are in the following datasheets:
                 * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
-                * New style   (6 byte ID): Samsung K9GAG08U0D (p.40)
+                * New style   (6 byte ID): Samsung K9GBG08U0M (p.40)
                 *
                 * Check for wraparound + Samsung ID + nonzero 6th byte
                 * to decide what to do.
@@ -2872,7 +3002,20 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                        mtd->writesize = 2048 << (extid & 0x03);
                        extid >>= 2;
                        /* Calc oobsize */
-                       mtd->oobsize = (extid & 0x03) == 0x01 ? 128 : 218;
+                       switch (extid & 0x03) {
+                       case 1:
+                               mtd->oobsize = 128;
+                               break;
+                       case 2:
+                               mtd->oobsize = 218;
+                               break;
+                       case 3:
+                               mtd->oobsize = 400;
+                               break;
+                       default:
+                               mtd->oobsize = 436;
+                               break;
+                       }
                        extid >>= 2;
                        /* Calc blocksize */
                        mtd->erasesize = (128 * 1024) <<
@@ -2900,7 +3043,35 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                mtd->writesize = type->pagesize;
                mtd->oobsize = mtd->writesize / 32;
                busw = type->options & NAND_BUSWIDTH_16;
+
+               /*
+                * Check for Spansion/AMD ID + repeating 5th, 6th byte since
+                * some Spansion chips have erasesize that conflicts with size
+                * listed in nand_ids table
+                * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
+                */
+               if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
+                               id_data[5] == 0x00 && id_data[6] == 0x00 &&
+                               id_data[7] == 0x00 && mtd->writesize == 512) {
+                       mtd->erasesize = 128 * 1024;
+                       mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
+               }
        }
+       /* Get chip options, preserve non chip based options */
+       chip->options &= ~NAND_CHIPOPTIONS_MSK;
+       chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+
+       /* Check if chip is a not a samsung device. Do not clear the
+        * options for chips which are not having an extended id.
+        */
+       if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
+               chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+ident_done:
+
+       /*
+        * Set chip as a default. Board drivers can override it, if necessary
+        */
+       chip->options |= NAND_NO_AUTOINCR;
 
        /* Try to identify manufacturer */
        for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
@@ -2915,7 +3086,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        if (busw != (chip->options & NAND_BUSWIDTH_16)) {
                printk(KERN_INFO "NAND device: Manufacturer ID:"
                       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
-                      dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+                      *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
                printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
                       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
                       busw ? 16 : 8);
@@ -2931,8 +3102,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                ffs(mtd->erasesize) - 1;
        if (chip->chipsize & 0xffffffff)
                chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
-       else
-               chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1;
+       else {
+               chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
+               chip->chip_shift += 32 - 1;
+       }
 
        /* Set the bad block position */
        if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16))
@@ -2940,27 +3113,12 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        else
                chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
 
-       /* Get chip options, preserve non chip based options */
-       chip->options &= ~NAND_CHIPOPTIONS_MSK;
-       chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
-
-       /*
-        * Set chip as a default. Board drivers can override it, if necessary
-        */
-       chip->options |= NAND_NO_AUTOINCR;
-
-       /* Check if chip is a not a samsung device. Do not clear the
-        * options for chips which are not having an extended id.
-        */
-       if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
-               chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
-
        /*
         * Bad block marker is stored in the last page of each block
         * on Samsung and Hynix MLC devices; stored in first two pages
         * of each block on Micron devices with 2KiB pages and on
-        * SLC Samsung, Hynix, and AMD/Spansion. All others scan only
-        * the first page.
+        * SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan
+        * only the first page.
         */
        if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
                        (*maf_id == NAND_MFR_SAMSUNG ||
@@ -2969,6 +3127,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
                                (*maf_id == NAND_MFR_SAMSUNG ||
                                 *maf_id == NAND_MFR_HYNIX ||
+                                *maf_id == NAND_MFR_TOSHIBA ||
                                 *maf_id == NAND_MFR_AMD)) ||
                        (mtd->writesize == 2048 &&
                         *maf_id == NAND_MFR_MICRON))
@@ -2994,9 +3153,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
                chip->cmdfunc = nand_command_lp;
 
+       /* TODO onfi flash name */
        printk(KERN_INFO "NAND device: Manufacturer ID:"
-              " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
-              nand_manuf_ids[maf_idx].name, type->name);
+               " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
+               nand_manuf_ids[maf_idx].name,
+       chip->onfi_version ? type->name : chip->onfi_params.model);
 
        return type;
 }
@@ -3015,7 +3176,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 int nand_scan_ident(struct mtd_info *mtd, int maxchips,
                    struct nand_flash_dev *table)
 {
-       int i, busw, nand_maf_id;
+       int i, busw, nand_maf_id, nand_dev_id;
        struct nand_chip *chip = mtd->priv;
        struct nand_flash_dev *type;
 
@@ -3025,7 +3186,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
        nand_set_defaults(chip, busw);
 
        /* Read the flash type */
-       type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table);
+       type = nand_get_flash_type(mtd, chip, busw,
+                               &nand_maf_id, &nand_dev_id, table);
 
        if (IS_ERR(type)) {
                if (!(chip->options & NAND_SCAN_SILENT_NODEV))
@@ -3043,7 +3205,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
                chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
                /* Read manufacturer and device IDs */
                if (nand_maf_id != chip->read_byte(mtd) ||
-                   type->id != chip->read_byte(mtd))
+                   nand_dev_id != chip->read_byte(mtd))
                        break;
        }
        if (i > 1)
@@ -3055,6 +3217,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 
        return 0;
 }
+EXPORT_SYMBOL(nand_scan_ident);
 
 
 /**
@@ -3219,7 +3382,7 @@ int nand_scan_tail(struct mtd_info *mtd)
         * mode
         */
        chip->ecc.steps = mtd->writesize / chip->ecc.size;
-       if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
+       if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
                printk(KERN_WARNING "Invalid ecc parameters\n");
                BUG();
        }
@@ -3231,7 +3394,7 @@ int nand_scan_tail(struct mtd_info *mtd)
         */
        if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
            !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
-               switch(chip->ecc.steps) {
+               switch (chip->ecc.steps) {
                case 2:
                        mtd->subpage_sft = 1;
                        break;
@@ -3283,10 +3446,11 @@ int nand_scan_tail(struct mtd_info *mtd)
        /* Build bad block table */
        return chip->scan_bbt(mtd);
 }
+EXPORT_SYMBOL(nand_scan_tail);
 
 /* is_module_text_address() isn't exported, and it's mostly a pointless
  test if this is a module _anyway_ -- they'd have to try _really_ hard
  to call us from in-kernel code if the core NAND support is modular. */
* test if this is a module _anyway_ -- they'd have to try _really_ hard
* to call us from in-kernel code if the core NAND support is modular. */
 #ifdef MODULE
 #define caller_is_module() (1)
 #else
@@ -3322,6 +3486,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
                ret = nand_scan_tail(mtd);
        return ret;
 }
+EXPORT_SYMBOL(nand_scan);
 
 /**
  * nand_release - [NAND Interface] Free resources held by the NAND device
@@ -3348,12 +3513,6 @@ void nand_release(struct mtd_info *mtd)
                        & NAND_BBT_DYNAMICSTRUCT)
                kfree(chip->badblock_pattern);
 }
-
-EXPORT_SYMBOL_GPL(nand_lock);
-EXPORT_SYMBOL_GPL(nand_unlock);
-EXPORT_SYMBOL_GPL(nand_scan);
-EXPORT_SYMBOL_GPL(nand_scan_ident);
-EXPORT_SYMBOL_GPL(nand_scan_tail);
 EXPORT_SYMBOL_GPL(nand_release);
 
 static int __init nand_base_init(void)
@@ -3371,5 +3530,6 @@ module_init(nand_base_init);
 module_exit(nand_base_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
 MODULE_DESCRIPTION("Generic NAND flash driver code");
index 5fedf4a..586b981 100644 (file)
  * Description:
  *
  * When nand_scan_bbt is called, then it tries to find the bad block table
- * depending on the options in the bbt descriptor(s). If a bbt is found
- * then the contents are read and the memory based bbt is created. If a
- * mirrored bbt is selected then the mirror is searched too and the
- * versions are compared. If the mirror has a greater version number
- * than the mirror bbt is used to build the memory based bbt.
+ * depending on the options in the BBT descriptor(s). If no flash based BBT
+ * (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory
+ * marked good / bad blocks. This information is used to create a memory BBT.
+ * Once a new bad block is discovered then the "factory" information is updated
+ * on the device.
+ * If a flash based BBT is specified then the function first tries to find the
+ * BBT on flash. If a BBT is found then the contents are read and the memory
+ * based BBT is created. If a mirrored BBT is selected then the mirror is
+ * searched too and the versions are compared. If the mirror has a greater
+ * version number than the mirror BBT is used to build the memory based BBT.
  * If the tables are not versioned, then we "or" the bad block information.
- * If one of the bbt's is out of date or does not exist it is (re)created.
- * If no bbt exists at all then the device is scanned for factory marked
+ * If one of the BBTs is out of date or does not exist it is (re)created.
+ * If no BBT exists at all then the device is scanned for factory marked
  * good / bad blocks and the bad block tables are created.
  *
- * For manufacturer created bbts like the one found on M-SYS DOC devices
- * the bbt is searched and read but never created
+ * For manufacturer created BBTs like the one found on M-SYS DOC devices
+ * the BBT is searched and read but never created
  *
- * The autogenerated bad block table is located in the last good blocks
+ * The auto generated bad block table is located in the last good blocks
  * of the device. The table is mirrored, so it can be updated eventually.
- * The table is marked in the oob area with an ident pattern and a version
- * number which indicates which of both tables is more up to date.
+ * The table is marked in the OOB area with an ident pattern and a version
+ * number which indicates which of both tables is more up to date. If the NAND
+ * controller needs the complete OOB area for the ECC information then the
+ * option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern
+ * and the version byte into the data area and the OOB area will remain
+ * untouched.
  *
  * The table uses 2 bits per block
- * 11b:        block is good
- * 00b:        block is factory marked bad
- * 01b, 10b:   block is marked bad due to wear
+ * 11b:                block is good
+ * 00b:                block is factory marked bad
+ * 01b, 10b:   block is marked bad due to wear
  *
  * The memory bad block table uses the following scheme:
  * 00b:                block is good
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
 
+static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
+{
+       int ret;
+
+       ret = memcmp(buf, td->pattern, td->len);
+       if (!ret)
+               return ret;
+       return -1;
+}
+
 /**
  * check_pattern - [GENERIC] check if a pattern is in the buffer
  * @buf:       the buffer to search
@@ -77,6 +96,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
        int i, end = 0;
        uint8_t *p = buf;
 
+       if (td->options & NAND_BBT_NO_OOB)
+               return check_pattern_no_oob(buf, td);
+
        end = paglen + td->offs;
        if (td->options & NAND_BBT_SCANEMPTY) {
                for (i = 0; i < end; i++) {
@@ -155,33 +177,64 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
        return 0;
 }
 
+/**
+ * add_marker_len - compute the length of the marker in data area
+ * @td:                BBT descriptor used for computation
+ *
+ * The length will be 0 if the markeris located in OOB area.
+ */
+static u32 add_marker_len(struct nand_bbt_descr *td)
+{
+       u32 len;
+
+       if (!(td->options & NAND_BBT_NO_OOB))
+               return 0;
+
+       len = td->len;
+       if (td->options & NAND_BBT_VERSION)
+               len++;
+       return len;
+}
+
 /**
  * read_bbt - [GENERIC] Read the bad block table starting from page
  * @mtd:       MTD device structure
  * @buf:       temporary buffer
  * @page:      the starting page
  * @num:       the number of bbt descriptors to read
- * @bits:      number of bits per block
+ * @td:                the bbt describtion table
  * @offs:      offset in the memory table
- * @reserved_block_code:       Pattern to identify reserved blocks
  *
  * Read the bad block table starting from page.
  *
  */
 static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
-                   int bits, int offs, int reserved_block_code)
+               struct nand_bbt_descr *td, int offs)
 {
        int res, i, j, act = 0;
        struct nand_chip *this = mtd->priv;
        size_t retlen, len, totlen;
        loff_t from;
+       int bits = td->options & NAND_BBT_NRBITS_MSK;
        uint8_t msk = (uint8_t) ((1 << bits) - 1);
+       u32 marker_len;
+       int reserved_block_code = td->reserved_block_code;
 
        totlen = (num * bits) >> 3;
+       marker_len = add_marker_len(td);
        from = ((loff_t) page) << this->page_shift;
 
        while (totlen) {
                len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
+               if (marker_len) {
+                       /*
+                        * In case the BBT marker is not in the OOB area it
+                        * will be just in the first page.
+                        */
+                       len -= marker_len;
+                       from += marker_len;
+                       marker_len = 0;
+               }
                res = mtd->read(mtd, from, len, &retlen, buf);
                if (res < 0) {
                        if (retlen != len) {
@@ -238,30 +291,47 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
 {
        struct nand_chip *this = mtd->priv;
        int res = 0, i;
-       int bits;
 
-       bits = td->options & NAND_BBT_NRBITS_MSK;
        if (td->options & NAND_BBT_PERCHIP) {
                int offs = 0;
                for (i = 0; i < this->numchips; i++) {
                        if (chip == -1 || chip == i)
-                               res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
+                               res = read_bbt(mtd, buf, td->pages[i],
+                                       this->chipsize >> this->bbt_erase_shift,
+                                       td, offs);
                        if (res)
                                return res;
                        offs += this->chipsize >> (this->bbt_erase_shift + 2);
                }
        } else {
-               res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
+               res = read_bbt(mtd, buf, td->pages[0],
+                               mtd->size >> this->bbt_erase_shift, td, 0);
                if (res)
                        return res;
        }
        return 0;
 }
 
+/*
+ * BBT marker is in the first page, no OOB.
+ */
+static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+                        struct nand_bbt_descr *td)
+{
+       size_t retlen;
+       size_t len;
+
+       len = td->len;
+       if (td->options & NAND_BBT_VERSION)
+               len++;
+
+       return mtd->read(mtd, offs, len, &retlen, buf);
+}
+
 /*
  * Scan read raw data from flash
  */
-static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
                         size_t len)
 {
        struct mtd_oob_ops ops;
@@ -294,6 +364,15 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
        return 0;
 }
 
+static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+                        size_t len, struct nand_bbt_descr *td)
+{
+       if (td->options & NAND_BBT_NO_OOB)
+               return scan_read_raw_data(mtd, buf, offs, td);
+       else
+               return scan_read_raw_oob(mtd, buf, offs, len);
+}
+
 /*
  * Scan write data with oob to flash
  */
@@ -312,6 +391,15 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
        return mtd->write_oob(mtd, offs, &ops);
 }
 
+static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+       u32 ver_offs = td->veroffs;
+
+       if (!(td->options & NAND_BBT_NO_OOB))
+               ver_offs += mtd->writesize;
+       return ver_offs;
+}
+
 /**
  * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
  * @mtd:       MTD device structure
@@ -331,8 +419,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
        /* Read the primary version, if available */
        if (td->options & NAND_BBT_VERSION) {
                scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
-                             mtd->writesize);
-               td->version[0] = buf[mtd->writesize + td->veroffs];
+                             mtd->writesize, td);
+               td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
                printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
                       td->pages[0], td->version[0]);
        }
@@ -340,8 +428,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
        /* Read the mirror version, if available */
        if (md && (md->options & NAND_BBT_VERSION)) {
                scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
-                             mtd->writesize);
-               md->version[0] = buf[mtd->writesize + md->veroffs];
+                             mtd->writesize, td);
+               md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
                printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
                       md->pages[0], md->version[0]);
        }
@@ -357,7 +445,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
 {
        int ret, j;
 
-       ret = scan_read_raw(mtd, buf, offs, readlen);
+       ret = scan_read_raw_oob(mtd, buf, offs, readlen);
        if (ret)
                return ret;
 
@@ -464,6 +552,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
        for (i = startblock; i < numblocks;) {
                int ret;
 
+               BUG_ON(bd->options & NAND_BBT_NO_OOB);
+
                if (bd->options & NAND_BBT_SCANALLPAGES)
                        ret = scan_block_full(mtd, bd, from, buf, readlen,
                                              scanlen, len);
@@ -545,11 +635,12 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
                        loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
 
                        /* Read first page */
-                       scan_read_raw(mtd, buf, offs, mtd->writesize);
+                       scan_read_raw(mtd, buf, offs, mtd->writesize, td);
                        if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
                                td->pages[i] = actblock << blocktopage;
                                if (td->options & NAND_BBT_VERSION) {
-                                       td->version[i] = buf[mtd->writesize + td->veroffs];
+                                       offs = bbt_get_ver_offs(mtd, td);
+                                       td->version[i] = buf[offs];
                                }
                                break;
                        }
@@ -733,12 +824,26 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                        memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
                        ooboffs = len + (pageoffs * mtd->oobsize);
 
+               } else if (td->options & NAND_BBT_NO_OOB) {
+                       ooboffs = 0;
+                       offs = td->len;
+                       /* the version byte */
+                       if (td->options & NAND_BBT_VERSION)
+                               offs++;
+                       /* Calc length */
+                       len = (size_t) (numblocks >> sft);
+                       len += offs;
+                       /* Make it page aligned ! */
+                       len = ALIGN(len, mtd->writesize);
+                       /* Preset the buffer with 0xff */
+                       memset(buf, 0xff, len);
+                       /* Pattern is located at the begin of first page */
+                       memcpy(buf, td->pattern, td->len);
                } else {
                        /* Calc length */
                        len = (size_t) (numblocks >> sft);
                        /* Make it page aligned ! */
-                       len = (len + (mtd->writesize - 1)) &
-                               ~(mtd->writesize - 1);
+                       len = ALIGN(len, mtd->writesize);
                        /* Preset the buffer with 0xff */
                        memset(buf, 0xff, len +
                               (len >> this->page_shift)* mtd->oobsize);
@@ -772,7 +877,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                if (res < 0)
                        goto outerr;
 
-               res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
+               res = scan_write_bbt(mtd, to, len, buf,
+                               td->options & NAND_BBT_NO_OOB ? NULL :
+                               &buf[len]);
                if (res < 0)
                        goto outerr;
 
@@ -892,7 +999,8 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
                        continue;
 
                /* Create the table in memory by scanning the chip(s) */
-               create_bbt(mtd, buf, bd, chipsel);
+               if (!(this->options & NAND_CREATE_EMPTY_BBT))
+                       create_bbt(mtd, buf, bd, chipsel);
 
                td->version[i] = 1;
                if (md)
@@ -982,6 +1090,49 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
        }
 }
 
+/**
+ * verify_bbt_descr - verify the bad block description
+ * @bd:                        the table to verify
+ *
+ * This functions performs a few sanity checks on the bad block description
+ * table.
+ */
+static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+       struct nand_chip *this = mtd->priv;
+       u32 pattern_len = bd->len;
+       u32 bits = bd->options & NAND_BBT_NRBITS_MSK;
+       u32 table_size;
+
+       if (!bd)
+               return;
+       BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) &&
+                       !(this->options & NAND_USE_FLASH_BBT));
+       BUG_ON(!bits);
+
+       if (bd->options & NAND_BBT_VERSION)
+               pattern_len++;
+
+       if (bd->options & NAND_BBT_NO_OOB) {
+               BUG_ON(!(this->options & NAND_USE_FLASH_BBT));
+               BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB));
+               BUG_ON(bd->offs);
+               if (bd->options & NAND_BBT_VERSION)
+                       BUG_ON(bd->veroffs != bd->len);
+               BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
+       }
+
+       if (bd->options & NAND_BBT_PERCHIP)
+               table_size = this->chipsize >> this->bbt_erase_shift;
+       else
+               table_size = mtd->size >> this->bbt_erase_shift;
+       table_size >>= 3;
+       table_size *= bits;
+       if (bd->options & NAND_BBT_NO_OOB)
+               table_size += pattern_len;
+       BUG_ON(table_size > (1 << this->bbt_erase_shift));
+}
+
 /**
  * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
  * @mtd:       MTD device structure
@@ -1023,6 +1174,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
                }
                return res;
        }
+       verify_bbt_descr(mtd, td);
+       verify_bbt_descr(mtd, md);
 
        /* Allocate a temporary buffer for one eraseblock incl. oob */
        len = (1 << this->bbt_erase_shift);
@@ -1166,6 +1319,26 @@ static struct nand_bbt_descr bbt_mirror_descr = {
        .pattern = mirror_pattern
 };
 
+static struct nand_bbt_descr bbt_main_no_bbt_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+               | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
+               | NAND_BBT_NO_OOB,
+       .len = 4,
+       .veroffs = 4,
+       .maxblocks = 4,
+       .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_no_bbt_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+               | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
+               | NAND_BBT_NO_OOB,
+       .len = 4,
+       .veroffs = 4,
+       .maxblocks = 4,
+       .pattern = mirror_pattern
+};
+
 #define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \
                NAND_BBT_SCANBYTE1AND6)
 /**
@@ -1236,8 +1409,13 @@ int nand_default_bbt(struct mtd_info *mtd)
        if (this->options & NAND_USE_FLASH_BBT) {
                /* Use the default pattern descriptors */
                if (!this->bbt_td) {
-                       this->bbt_td = &bbt_main_descr;
-                       this->bbt_md = &bbt_mirror_descr;
+                       if (this->options & NAND_USE_FLASH_BBT_NO_OOB) {
+                               this->bbt_td = &bbt_main_no_bbt_descr;
+                               this->bbt_md = &bbt_mirror_no_bbt_descr;
+                       } else {
+                               this->bbt_td = &bbt_main_descr;
+                               this->bbt_md = &bbt_mirror_descr;
+                       }
                }
                if (!this->badblock_pattern) {
                        this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
index c65f190..00cf1b0 100644 (file)
@@ -75,9 +75,13 @@ struct nand_flash_dev nand_flash_ids[] = {
 
        /*512 Megabit */
        {"NAND 64MiB 1,8V 8-bit",       0xA2, 0,  64, 0, LP_OPTIONS},
+       {"NAND 64MiB 1,8V 8-bit",       0xA0, 0,  64, 0, LP_OPTIONS},
        {"NAND 64MiB 3,3V 8-bit",       0xF2, 0,  64, 0, LP_OPTIONS},
+       {"NAND 64MiB 3,3V 8-bit",       0xD0, 0,  64, 0, LP_OPTIONS},
        {"NAND 64MiB 1,8V 16-bit",      0xB2, 0,  64, 0, LP_OPTIONS16},
+       {"NAND 64MiB 1,8V 16-bit",      0xB0, 0,  64, 0, LP_OPTIONS16},
        {"NAND 64MiB 3,3V 16-bit",      0xC2, 0,  64, 0, LP_OPTIONS16},
+       {"NAND 64MiB 3,3V 16-bit",      0xC0, 0,  64, 0, LP_OPTIONS16},
 
        /* 1 Gigabit */
        {"NAND 128MiB 1,8V 8-bit",      0xA1, 0, 128, 0, LP_OPTIONS},
@@ -112,7 +116,34 @@ struct nand_flash_dev nand_flash_ids[] = {
        {"NAND 2GiB 3,3V 16-bit",       0xC5, 0, 2048, 0, LP_OPTIONS16},
 
        /* 32 Gigabit */
+       {"NAND 4GiB 1,8V 8-bit",        0xA7, 0, 4096, 0, LP_OPTIONS},
        {"NAND 4GiB 3,3V 8-bit",        0xD7, 0, 4096, 0, LP_OPTIONS},
+       {"NAND 4GiB 1,8V 16-bit",       0xB7, 0, 4096, 0, LP_OPTIONS16},
+       {"NAND 4GiB 3,3V 16-bit",       0xC7, 0, 4096, 0, LP_OPTIONS16},
+
+       /* 64 Gigabit */
+       {"NAND 8GiB 1,8V 8-bit",        0xAE, 0, 8192, 0, LP_OPTIONS},
+       {"NAND 8GiB 3,3V 8-bit",        0xDE, 0, 8192, 0, LP_OPTIONS},
+       {"NAND 8GiB 1,8V 16-bit",       0xBE, 0, 8192, 0, LP_OPTIONS16},
+       {"NAND 8GiB 3,3V 16-bit",       0xCE, 0, 8192, 0, LP_OPTIONS16},
+
+       /* 128 Gigabit */
+       {"NAND 16GiB 1,8V 8-bit",       0x1A, 0, 16384, 0, LP_OPTIONS},
+       {"NAND 16GiB 3,3V 8-bit",       0x3A, 0, 16384, 0, LP_OPTIONS},
+       {"NAND 16GiB 1,8V 16-bit",      0x2A, 0, 16384, 0, LP_OPTIONS16},
+       {"NAND 16GiB 3,3V 16-bit",      0x4A, 0, 16384, 0, LP_OPTIONS16},
+
+       /* 256 Gigabit */
+       {"NAND 32GiB 1,8V 8-bit",       0x1C, 0, 32768, 0, LP_OPTIONS},
+       {"NAND 32GiB 3,3V 8-bit",       0x3C, 0, 32768, 0, LP_OPTIONS},
+       {"NAND 32GiB 1,8V 16-bit",      0x2C, 0, 32768, 0, LP_OPTIONS16},
+       {"NAND 32GiB 3,3V 16-bit",      0x4C, 0, 32768, 0, LP_OPTIONS16},
+
+       /* 512 Gigabit */
+       {"NAND 64GiB 1,8V 8-bit",       0x1E, 0, 65536, 0, LP_OPTIONS},
+       {"NAND 64GiB 3,3V 8-bit",       0x3E, 0, 65536, 0, LP_OPTIONS},
+       {"NAND 64GiB 1,8V 16-bit",      0x2E, 0, 65536, 0, LP_OPTIONS16},
+       {"NAND 64GiB 3,3V 16-bit",      0x4E, 0, 65536, 0, LP_OPTIONS16},
 
        /*
         * Renesas AND 1 Gigabit. Those chips do not support extended id and
index c25648b..a6a73aa 100644 (file)
@@ -107,6 +107,7 @@ static char *gravepages = NULL;
 static unsigned int rptwear = 0;
 static unsigned int overridesize = 0;
 static char *cache_file = NULL;
+static unsigned int bbt;
 
 module_param(first_id_byte,  uint, 0400);
 module_param(second_id_byte, uint, 0400);
@@ -130,6 +131,7 @@ module_param(gravepages,     charp, 0400);
 module_param(rptwear,        uint, 0400);
 module_param(overridesize,   uint, 0400);
 module_param(cache_file,     charp, 0400);
+module_param(bbt,           uint, 0400);
 
 MODULE_PARM_DESC(first_id_byte,  "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
 MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
@@ -162,6 +164,7 @@ MODULE_PARM_DESC(overridesize,   "Specifies the NAND Flash size overriding the I
                                 "The size is specified in erase blocks and as the exponent of a power of two"
                                 " e.g. 5 means a size of 32 erase blocks");
 MODULE_PARM_DESC(cache_file,     "File to use to cache nand pages instead of memory");
+MODULE_PARM_DESC(bbt,           "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
 
 /* The largest possible page size */
 #define NS_LARGEST_PAGE_SIZE   4096
@@ -2264,6 +2267,18 @@ static int __init ns_init_module(void)
        /* and 'badblocks' parameters to work */
        chip->options   |= NAND_SKIP_BBTSCAN;
 
+       switch (bbt) {
+       case 2:
+                chip->options |= NAND_USE_FLASH_BBT_NO_OOB;
+       case 1:
+                chip->options |= NAND_USE_FLASH_BBT;
+       case 0:
+               break;
+       default:
+               NS_ERR("bbt has to be 0..2\n");
+               retval = -EINVAL;
+               goto error;
+       }
        /*
         * Perform minimum nandsim structure initialization to handle
         * the initial ID read command correctly
@@ -2321,10 +2336,10 @@ static int __init ns_init_module(void)
        if ((retval = init_nandsim(nsmtd)) != 0)
                goto err_exit;
 
-       if ((retval = parse_badblocks(nand, nsmtd)) != 0)
+       if ((retval = nand_default_bbt(nsmtd)) != 0)
                goto err_exit;
 
-       if ((retval = nand_default_bbt(nsmtd)) != 0)
+       if ((retval = parse_badblocks(nand, nsmtd)) != 0)
                goto err_exit;
 
        /* Register NAND partitions */
index 510554e..c9ae0a5 100644 (file)
@@ -229,7 +229,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
                                const struct of_device_id *match)
 {
        struct ndfc_controller *ndfc = &ndfc_ctrl;
-       const u32 *reg;
+       const __be32 *reg;
        u32 ccr;
        int err, len;
 
@@ -244,7 +244,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
                dev_err(&ofdev->dev, "unable read reg property (%d)\n", len);
                return -ENOENT;
        }
-       ndfc->chip_select = reg[0];
+       ndfc->chip_select = be32_to_cpu(reg[0]);
 
        ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0);
        if (!ndfc->ndfcbase) {
@@ -257,7 +257,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
        /* It is ok if ccr does not exist - just default to 0 */
        reg = of_get_property(ofdev->dev.of_node, "ccr", NULL);
        if (reg)
-               ccr |= *reg;
+               ccr |= be32_to_cpup(reg);
 
        out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
 
@@ -265,7 +265,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
        reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL);
        if (reg) {
                int offset = NDFC_BCFG0 + (ndfc->chip_select << 2);
-               out_be32(ndfc->ndfcbase + offset, *reg);
+               out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg));
        }
 
        err = ndfc_chip_init(ndfc, ofdev->dev.of_node);
index 513e0a7..cd41c58 100644 (file)
@@ -111,11 +111,11 @@ static int use_dma = 1;
 module_param(use_dma, bool, 0);
 MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
 #else
-const int use_dma;
+static const int use_dma;
 #endif
 #else
 const int use_prefetch;
-const int use_dma;
+static const int use_dma;
 #endif
 
 struct omap_nand_info {
index 4d01cda..17f8518 100644 (file)
@@ -117,7 +117,7 @@ struct pxa3xx_nand_info {
        struct nand_chip        nand_chip;
 
        struct platform_device   *pdev;
-       const struct pxa3xx_nand_flash *flash_info;
+       struct pxa3xx_nand_cmdset *cmdset;
 
        struct clk              *clk;
        void __iomem            *mmio_base;
@@ -131,6 +131,7 @@ struct pxa3xx_nand_info {
        int                     drcmr_cmd;
 
        unsigned char           *data_buff;
+       unsigned char           *oob_buff;
        dma_addr_t              data_buff_phys;
        size_t                  data_buff_size;
        int                     data_dma_ch;
@@ -149,7 +150,8 @@ struct pxa3xx_nand_info {
        int                     use_ecc;        /* use HW ECC ? */
        int                     use_dma;        /* use DMA ? */
 
-       size_t                  data_size;      /* data size in FIFO */
+       unsigned int            page_size;      /* page size of attached chip */
+       unsigned int            data_size;      /* data size in FIFO */
        int                     retcode;
        struct completion       cmd_complete;
 
@@ -158,6 +160,10 @@ struct pxa3xx_nand_info {
        uint32_t                ndcb1;
        uint32_t                ndcb2;
 
+       /* timing calcuted from setting */
+       uint32_t                ndtr0cs0;
+       uint32_t                ndtr1cs0;
+
        /* calculated from pxa3xx_nand_flash data */
        size_t          oob_size;
        size_t          read_id_bytes;
@@ -174,23 +180,7 @@ MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
  * Default NAND flash controller configuration setup by the
  * bootloader. This configuration is used only when pdata->keep_config is set
  */
-static struct pxa3xx_nand_timing default_timing;
-static struct pxa3xx_nand_flash default_flash;
-
-static struct pxa3xx_nand_cmdset smallpage_cmdset = {
-       .read1          = 0x0000,
-       .read2          = 0x0050,
-       .program        = 0x1080,
-       .read_status    = 0x0070,
-       .read_id        = 0x0090,
-       .erase          = 0xD060,
-       .reset          = 0x00FF,
-       .lock           = 0x002A,
-       .unlock         = 0x2423,
-       .lock_status    = 0x007A,
-};
-
-static struct pxa3xx_nand_cmdset largepage_cmdset = {
+static struct pxa3xx_nand_cmdset default_cmdset = {
        .read1          = 0x3000,
        .read2          = 0x0050,
        .program        = 0x1080,
@@ -203,142 +193,27 @@ static struct pxa3xx_nand_cmdset largepage_cmdset = {
        .lock_status    = 0x007A,
 };
 
-#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
-static struct pxa3xx_nand_timing samsung512MbX16_timing = {
-       .tCH    = 10,
-       .tCS    = 0,
-       .tWH    = 20,
-       .tWP    = 40,
-       .tRH    = 30,
-       .tRP    = 40,
-       .tR     = 11123,
-       .tWHR   = 110,
-       .tAR    = 10,
-};
-
-static struct pxa3xx_nand_flash samsung512MbX16 = {
-       .timing         = &samsung512MbX16_timing,
-       .cmdset         = &smallpage_cmdset,
-       .page_per_block = 32,
-       .page_size      = 512,
-       .flash_width    = 16,
-       .dfc_width      = 16,
-       .num_blocks     = 4096,
-       .chip_id        = 0x46ec,
-};
-
-static struct pxa3xx_nand_flash samsung2GbX8 = {
-       .timing         = &samsung512MbX16_timing,
-       .cmdset         = &smallpage_cmdset,
-       .page_per_block = 64,
-       .page_size      = 2048,
-       .flash_width    = 8,
-       .dfc_width      = 8,
-       .num_blocks     = 2048,
-       .chip_id        = 0xdaec,
+static struct pxa3xx_nand_timing timing[] = {
+       { 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
+       { 10,  0, 20,  40, 30,  40, 11123, 110, 10, },
+       { 10, 25, 15,  25, 15,  30, 25000,  60, 10, },
+       { 10, 35, 15,  25, 15,  25, 25000,  60, 10, },
 };
 
-static struct pxa3xx_nand_flash samsung32GbX8 = {
-       .timing         = &samsung512MbX16_timing,
-       .cmdset         = &smallpage_cmdset,
-       .page_per_block = 128,
-       .page_size      = 4096,
-       .flash_width    = 8,
-       .dfc_width      = 8,
-       .num_blocks     = 8192,
-       .chip_id        = 0xd7ec,
+static struct pxa3xx_nand_flash builtin_flash_types[] = {
+       {      0,   0, 2048,  8,  8,    0, &default_cmdset, &timing[0] },
+       { 0x46ec,  32,  512, 16, 16, 4096, &default_cmdset, &timing[1] },
+       { 0xdaec,  64, 2048,  8,  8, 2048, &default_cmdset, &timing[1] },
+       { 0xd7ec, 128, 4096,  8,  8, 8192, &default_cmdset, &timing[1] },
+       { 0xa12c,  64, 2048,  8,  8, 1024, &default_cmdset, &timing[2] },
+       { 0xb12c,  64, 2048, 16, 16, 1024, &default_cmdset, &timing[2] },
+       { 0xdc2c,  64, 2048,  8,  8, 4096, &default_cmdset, &timing[2] },
+       { 0xcc2c,  64, 2048, 16, 16, 4096, &default_cmdset, &timing[2] },
+       { 0xba20,  64, 2048, 16, 16, 2048, &default_cmdset, &timing[3] },
 };
 
-static struct pxa3xx_nand_timing micron_timing = {
-       .tCH    = 10,
-       .tCS    = 25,
-       .tWH    = 15,
-       .tWP    = 25,
-       .tRH    = 15,
-       .tRP    = 30,
-       .tR     = 25000,
-       .tWHR   = 60,
-       .tAR    = 10,
-};
-
-static struct pxa3xx_nand_flash micron1GbX8 = {
-       .timing         = &micron_timing,
-       .cmdset         = &largepage_cmdset,
-       .page_per_block = 64,
-       .page_size      = 2048,
-       .flash_width    = 8,
-       .dfc_width      = 8,
-       .num_blocks     = 1024,
-       .chip_id        = 0xa12c,
-};
-
-static struct pxa3xx_nand_flash micron1GbX16 = {
-       .timing         = &micron_timing,
-       .cmdset         = &largepage_cmdset,
-       .page_per_block = 64,
-       .page_size      = 2048,
-       .flash_width    = 16,
-       .dfc_width      = 16,
-       .num_blocks     = 1024,
-       .chip_id        = 0xb12c,
-};
-
-static struct pxa3xx_nand_flash micron4GbX8 = {
-       .timing         = &micron_timing,
-       .cmdset         = &largepage_cmdset,
-       .page_per_block = 64,
-       .page_size      = 2048,
-       .flash_width    = 8,
-       .dfc_width      = 8,
-       .num_blocks     = 4096,
-       .chip_id        = 0xdc2c,
-};
-
-static struct pxa3xx_nand_flash micron4GbX16 = {
-       .timing         = &micron_timing,
-       .cmdset         = &largepage_cmdset,
-       .page_per_block = 64,
-       .page_size      = 2048,
-       .flash_width    = 16,
-       .dfc_width      = 16,
-       .num_blocks     = 4096,
-       .chip_id        = 0xcc2c,
-};
-
-static struct pxa3xx_nand_timing stm2GbX16_timing = {
-       .tCH = 10,
-       .tCS = 35,
-       .tWH = 15,
-       .tWP = 25,
-       .tRH = 15,
-       .tRP = 25,
-       .tR = 25000,
-       .tWHR = 60,
-       .tAR = 10,
-};
-
-static struct pxa3xx_nand_flash stm2GbX16 = {
-       .timing = &stm2GbX16_timing,
-       .cmdset = &largepage_cmdset,
-       .page_per_block = 64,
-       .page_size = 2048,
-       .flash_width = 16,
-       .dfc_width = 16,
-       .num_blocks = 2048,
-       .chip_id = 0xba20,
-};
-
-static struct pxa3xx_nand_flash *builtin_flash_types[] = {
-       &samsung512MbX16,
-       &samsung2GbX8,
-       &samsung32GbX8,
-       &micron1GbX8,
-       &micron1GbX16,
-       &micron4GbX8,
-       &micron4GbX16,
-       &stm2GbX16,
-};
-#endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */
+/* Define a default flash type setting serve as flash detecting only */
+#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
 
 #define NDTR0_tCH(c)   (min((c), 7) << 19)
 #define NDTR0_tCS(c)   (min((c), 7) << 16)
@@ -351,23 +226,9 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = {
 #define NDTR1_tWHR(c)  (min((c), 15) << 4)
 #define NDTR1_tAR(c)   (min((c), 15) << 0)
 
-#define tCH_NDTR0(r)   (((r) >> 19) & 0x7)
-#define tCS_NDTR0(r)   (((r) >> 16) & 0x7)
-#define tWH_NDTR0(r)   (((r) >> 11) & 0x7)
-#define tWP_NDTR0(r)   (((r) >> 8) & 0x7)
-#define tRH_NDTR0(r)   (((r) >> 3) & 0x7)
-#define tRP_NDTR0(r)   (((r) >> 0) & 0x7)
-
-#define tR_NDTR1(r)    (((r) >> 16) & 0xffff)
-#define tWHR_NDTR1(r)  (((r) >> 4) & 0xf)
-#define tAR_NDTR1(r)   (((r) >> 0) & 0xf)
-
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)      (int)((ns) * (clk / 1000000) / 1000)
 
-/* convert nand flash controller clock cycles to nano-seconds */
-#define cycle2ns(c, clk)       ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000))
-
 static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
                                   const struct pxa3xx_nand_timing *t)
 {
@@ -385,6 +246,8 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
                NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
                NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
 
+       info->ndtr0cs0 = ndtr0;
+       info->ndtr1cs0 = ndtr1;
        nand_writel(info, NDTR0CS0, ndtr0);
        nand_writel(info, NDTR1CS0, ndtr1);
 }
@@ -408,23 +271,31 @@ static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event)
        return -ETIMEDOUT;
 }
 
-static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
-                       uint16_t cmd, int column, int page_addr)
+static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
 {
-       const struct pxa3xx_nand_flash *f = info->flash_info;
-       const struct pxa3xx_nand_cmdset *cmdset = f->cmdset;
+       int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
 
-       /* calculate data size */
-       switch (f->page_size) {
+       info->data_size = info->page_size;
+       if (!oob_enable) {
+               info->oob_size = 0;
+               return;
+       }
+
+       switch (info->page_size) {
        case 2048:
-               info->data_size = (info->use_ecc) ? 2088 : 2112;
+               info->oob_size = (info->use_ecc) ? 40 : 64;
                break;
        case 512:
-               info->data_size = (info->use_ecc) ? 520 : 528;
+               info->oob_size = (info->use_ecc) ? 8 : 16;
                break;
-       default:
-               return -EINVAL;
        }
+}
+
+static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
+               uint16_t cmd, int column, int page_addr)
+{
+       const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
+       pxa3xx_set_datasize(info);
 
        /* generate values for NDCBx registers */
        info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
@@ -463,12 +334,13 @@ static int prepare_erase_cmd(struct pxa3xx_nand_info *info,
 
 static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd)
 {
-       const struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset;
+       const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
 
        info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
        info->ndcb1 = 0;
        info->ndcb2 = 0;
 
+       info->oob_size = 0;
        if (cmd == cmdset->read_id) {
                info->ndcb0 |= NDCB0_CMD_TYPE(3);
                info->data_size = 8;
@@ -537,6 +409,9 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
        case STATE_PIO_WRITING:
                __raw_writesl(info->mmio_base + NDDB, info->data_buff,
                                DIV_ROUND_UP(info->data_size, 4));
+               if (info->oob_size > 0)
+                       __raw_writesl(info->mmio_base + NDDB, info->oob_buff,
+                                       DIV_ROUND_UP(info->oob_size, 4));
 
                enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
 
@@ -549,6 +424,9 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
        case STATE_PIO_READING:
                __raw_readsl(info->mmio_base + NDDB, info->data_buff,
                                DIV_ROUND_UP(info->data_size, 4));
+               if (info->oob_size > 0)
+                       __raw_readsl(info->mmio_base + NDDB, info->oob_buff,
+                                       DIV_ROUND_UP(info->oob_size, 4));
                break;
        default:
                printk(KERN_ERR "%s: invalid state %d\n", __func__,
@@ -563,7 +441,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
 static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out)
 {
        struct pxa_dma_desc *desc = info->data_desc;
-       int dma_len = ALIGN(info->data_size, 32);
+       int dma_len = ALIGN(info->data_size + info->oob_size, 32);
 
        desc->ddadr = DDADR_STOP;
        desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
@@ -700,8 +578,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
                                int column, int page_addr)
 {
        struct pxa3xx_nand_info *info = mtd->priv;
-       const struct pxa3xx_nand_flash *flash_info = info->flash_info;
-       const struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset;
+       const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
        int ret;
 
        info->use_dma = (use_dma) ? 1 : 0;
@@ -925,8 +802,7 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd,
 
 static int __readid(struct pxa3xx_nand_info *info, uint32_t *id)
 {
-       const struct pxa3xx_nand_flash *f = info->flash_info;
-       const struct pxa3xx_nand_cmdset *cmdset = f->cmdset;
+       const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
        uint32_t ndcr;
        uint8_t  id_buff[8];
 
@@ -968,7 +844,9 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
                return -EINVAL;
 
        /* calculate flash information */
-       info->oob_size = (f->page_size == 2048) ? 64 : 16;
+       info->cmdset = f->cmdset;
+       info->page_size = f->page_size;
+       info->oob_buff = info->data_buff + f->page_size;
        info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
 
        /* calculate addressing information */
@@ -992,49 +870,20 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
        info->reg_ndcr = ndcr;
 
        pxa3xx_nand_set_timing(info, f->timing);
-       info->flash_info = f;
        return 0;
 }
 
-static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info,
-                                     struct pxa3xx_nand_timing *t)
-{
-       unsigned long nand_clk = clk_get_rate(info->clk);
-       uint32_t ndtr0 = nand_readl(info, NDTR0CS0);
-       uint32_t ndtr1 = nand_readl(info, NDTR1CS0);
-
-       t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk);
-       t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk);
-       t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk);
-       t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk);
-       t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk);
-       t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk);
-
-       t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk);
-       t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk);
-       t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk);
-}
-
 static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
 {
        uint32_t ndcr = nand_readl(info, NDCR);
        struct nand_flash_dev *type = NULL;
-       uint32_t id = -1;
+       uint32_t id = -1, page_per_block, num_blocks;
        int i;
 
-       default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
-       default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
-       default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8;
-       default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8;
-
-       if (default_flash.page_size == 2048)
-               default_flash.cmdset = &largepage_cmdset;
-       else
-               default_flash.cmdset = &smallpage_cmdset;
-
+       page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
+       info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
        /* set info fields needed to __readid */
-       info->flash_info = &default_flash;
-       info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2;
+       info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
        info->reg_ndcr = ndcr;
 
        if (__readid(info, &id))
@@ -1053,21 +902,20 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
                return -ENODEV;
 
        /* fill the missing flash information */
-       i = __ffs(default_flash.page_per_block * default_flash.page_size);
-       default_flash.num_blocks = type->chipsize << (20 - i);
-
-       info->oob_size = (default_flash.page_size == 2048) ? 64 : 16;
+       i = __ffs(page_per_block * info->page_size);
+       num_blocks = type->chipsize << (20 - i);
 
        /* calculate addressing information */
-       info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1;
+       info->col_addr_cycles = (info->page_size == 2048) ? 2 : 1;
 
-       if (default_flash.num_blocks * default_flash.page_per_block > 65536)
+       if (num_blocks * page_per_block > 65536)
                info->row_addr_cycles = 3;
        else
                info->row_addr_cycles = 2;
 
-       pxa3xx_nand_detect_timing(info, &default_timing);
-       default_flash.timing = &default_timing;
+       info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+       info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+       info->cmdset = &default_cmdset;
 
        return 0;
 }
@@ -1083,38 +931,29 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
                if (pxa3xx_nand_detect_config(info) == 0)
                        return 0;
 
-       for (i = 0; i<pdata->num_flash; ++i) {
-               f = pdata->flash + i;
-
-               if (pxa3xx_nand_config_flash(info, f))
-                       continue;
-
-               if (__readid(info, &id))
-                       continue;
-
-               if (id == f->chip_id)
-                       return 0;
-       }
-
-#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
-       for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) {
-
-               f = builtin_flash_types[i];
-
-               if (pxa3xx_nand_config_flash(info, f))
-                       continue;
-
-               if (__readid(info, &id))
-                       continue;
-
-               if (id == f->chip_id)
+       /* we use default timing to detect id */
+       f = DEFAULT_FLASH_TYPE;
+       pxa3xx_nand_config_flash(info, f);
+       if (__readid(info, &id))
+               goto fail_detect;
+
+       for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) {
+               /* we first choose the flash definition from platfrom */
+               if (i < pdata->num_flash)
+                       f = pdata->flash + i;
+               else
+                       f = &builtin_flash_types[i - pdata->num_flash + 1];
+               if (f->chip_id == id) {
+                       dev_info(&info->pdev->dev, "detect chip id: 0x%x\n", id);
+                       pxa3xx_nand_config_flash(info, f);
                        return 0;
+               }
        }
-#endif
 
        dev_warn(&info->pdev->dev,
                 "failed to detect configured nand flash; found %04x instead of\n",
                 id);
+fail_detect:
        return -ENODEV;
 }
 
@@ -1177,10 +1016,9 @@ static struct nand_ecclayout hw_largepage_ecclayout = {
 static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
                                 struct pxa3xx_nand_info *info)
 {
-       const struct pxa3xx_nand_flash *f = info->flash_info;
        struct nand_chip *this = &info->nand_chip;
 
-       this->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16: 0;
+       this->options = (info->reg_ndcr & NDCR_DWIDTH_C) ? NAND_BUSWIDTH_16: 0;
 
        this->waitfunc          = pxa3xx_nand_waitfunc;
        this->select_chip       = pxa3xx_nand_select_chip;
@@ -1196,9 +1034,9 @@ static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
        this->ecc.hwctl         = pxa3xx_nand_ecc_hwctl;
        this->ecc.calculate     = pxa3xx_nand_ecc_calculate;
        this->ecc.correct       = pxa3xx_nand_ecc_correct;
-       this->ecc.size          = f->page_size;
+       this->ecc.size          = info->page_size;
 
-       if (f->page_size == 2048)
+       if (info->page_size == 2048)
                this->ecc.layout = &hw_largepage_ecclayout;
        else
                this->ecc.layout = &hw_smallpage_ecclayout;
@@ -1411,9 +1249,11 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
        struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev);
        struct pxa3xx_nand_info *info = mtd->priv;
 
+       nand_writel(info, NDTR0CS0, info->ndtr0cs0);
+       nand_writel(info, NDTR1CS0, info->ndtr1cs0);
        clk_enable(info->clk);
 
-       return pxa3xx_nand_config_flash(info, info->flash_info);
+       return 0;
 }
 #else
 #define pxa3xx_nand_suspend    NULL
index 5169ca6..d9d7efb 100644 (file)
@@ -757,11 +757,6 @@ static irqreturn_t r852_irq(int irq, void *data)
 
        spin_lock_irqsave(&dev->irqlock, flags);
 
-       /* We can recieve shared interrupt while pci is suspended
-               in that case reads will return 0xFFFFFFFF.... */
-       if (dev->insuspend)
-               goto out;
-
        /* handle card detection interrupts first */
        card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
        r852_write_reg(dev, R852_CARD_IRQ_STA, card_status);
@@ -1035,7 +1030,6 @@ void r852_shutdown(struct pci_dev *pci_dev)
 int r852_suspend(struct device *device)
 {
        struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
-       unsigned long flags;
 
        if (dev->ctlreg & R852_CTL_CARDENABLE)
                return -EBUSY;
@@ -1047,43 +1041,22 @@ int r852_suspend(struct device *device)
        r852_disable_irqs(dev);
        r852_engine_disable(dev);
 
-       spin_lock_irqsave(&dev->irqlock, flags);
-       dev->insuspend = 1;
-       spin_unlock_irqrestore(&dev->irqlock, flags);
-
-       /* At that point, even if interrupt handler is running, it will quit */
-       /* So wait for this to happen explictly */
-       synchronize_irq(dev->irq);
-
        /* If card was pulled off just during the suspend, which is very
                unlikely, we will remove it on resume, it too late now
                anyway... */
        dev->card_unstable = 0;
-
-       pci_save_state(to_pci_dev(device));
-       return pci_prepare_to_sleep(to_pci_dev(device));
+       return 0;
 }
 
 int r852_resume(struct device *device)
 {
        struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
-       unsigned long flags;
-
-       /* Turn on the hardware */
-       pci_back_from_sleep(to_pci_dev(device));
-       pci_restore_state(to_pci_dev(device));
 
        r852_disable_irqs(dev);
        r852_card_update_present(dev);
        r852_engine_disable(dev);
 
 
-       /* Now its safe for IRQ to run */
-       spin_lock_irqsave(&dev->irqlock, flags);
-       dev->insuspend = 0;
-       spin_unlock_irqrestore(&dev->irqlock, flags);
-
-
        /* If card status changed, just do the work */
        if (dev->card_detected != dev->card_registred) {
                dbg("card was %s during low power state",
@@ -1121,7 +1094,6 @@ MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl);
 
 SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume);
 
-
 static struct pci_driver r852_pci_driver = {
        .name           = DRV_NAME,
        .id_table       = r852_pci_id_tbl,
index 8096cc2..e6a21d9 100644 (file)
@@ -140,8 +140,6 @@ struct r852_device {
        /* interrupt handling */
        spinlock_t irqlock;             /* IRQ protecting lock */
        int irq;                        /* irq num */
-       int insuspend;                  /* device is suspended */
-
        /* misc */
        void *tmp_buffer;               /* temporary buffer */
        uint8_t ctlreg;                 /* cached contents of control reg */
index 7bd171e..a996718 100644 (file)
@@ -44,7 +44,7 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
        pp = NULL;
        i = 0;
        while ((pp = of_get_next_child(node, pp))) {
-               const u32 *reg;
+               const __be32 *reg;
                int len;
 
                reg = of_get_property(pp, "reg", &len);
index 3f32289..4dbd0f5 100644 (file)
@@ -32,10 +32,11 @@ config MTD_ONENAND_OMAP2
 
 config MTD_ONENAND_SAMSUNG
         tristate "OneNAND on Samsung SOC controller support"
-        depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210
+        depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5PV310
         help
-          Support for a OneNAND flash device connected to an Samsung SOC
-          S3C64XX/S5PC1XX controller.
+          Support for a OneNAND flash device connected to an Samsung SOC.
+          S3C64XX/S5PC100 use command mapping method.
+          S5PC110/S5PC210 use generic OneNAND method.
 
 config MTD_ONENAND_OTP
        bool "OneNAND OTP Support"
index a2bb520..6b3a875 100644 (file)
@@ -3365,18 +3365,19 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 static void onenand_check_features(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
-       unsigned int density, process;
+       unsigned int density, process, numbufs;
 
        /* Lock scheme depends on density and process */
        density = onenand_get_density(this->device_id);
        process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
+       numbufs = this->read_word(this->base + ONENAND_REG_NUM_BUFFERS) >> 8;
 
        /* Lock scheme */
        switch (density) {
        case ONENAND_DEVICE_DENSITY_4Gb:
                if (ONENAND_IS_DDP(this))
                        this->options |= ONENAND_HAS_2PLANE;
-               else
+               else if (numbufs == 1)
                        this->options |= ONENAND_HAS_4KB_PAGE;
 
        case ONENAND_DEVICE_DENSITY_2Gb:
@@ -4027,7 +4028,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        mtd->ecclayout = this->ecclayout;
 
        /* Fill in remaining MTD driver data */
-       mtd->type = MTD_NANDFLASH;
+       mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
        mtd->erase = onenand_erase;
        mtd->point = NULL;
index a460f1b..0de7a05 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
 
 #include <asm/mach/flash.h>
 #include <plat/regs-onenand.h>
@@ -58,7 +59,7 @@ enum soc_type {
 #define MAP_11                         (0x3)
 
 #define S3C64XX_CMD_MAP_SHIFT          24
-#define S5PC1XX_CMD_MAP_SHIFT          26
+#define S5PC100_CMD_MAP_SHIFT          26
 
 #define S3C6400_FBA_SHIFT              10
 #define S3C6400_FPA_SHIFT              4
@@ -81,6 +82,17 @@ enum soc_type {
 #define S5PC110_DMA_TRANS_CMD          0x418
 #define S5PC110_DMA_TRANS_STATUS       0x41C
 #define S5PC110_DMA_TRANS_DIR          0x420
+#define S5PC110_INTC_DMA_CLR           0x1004
+#define S5PC110_INTC_ONENAND_CLR       0x1008
+#define S5PC110_INTC_DMA_MASK          0x1024
+#define S5PC110_INTC_ONENAND_MASK      0x1028
+#define S5PC110_INTC_DMA_PEND          0x1044
+#define S5PC110_INTC_ONENAND_PEND      0x1048
+#define S5PC110_INTC_DMA_STATUS                0x1064
+#define S5PC110_INTC_ONENAND_STATUS    0x1068
+
+#define S5PC110_INTC_DMA_TD            (1 << 24)
+#define S5PC110_INTC_DMA_TE            (1 << 16)
 
 #define S5PC110_DMA_CFG_SINGLE         (0x0 << 16)
 #define S5PC110_DMA_CFG_4BURST         (0x2 << 16)
@@ -134,6 +146,7 @@ struct s3c_onenand {
        void __iomem    *dma_addr;
        struct resource *dma_res;
        unsigned long   phys_base;
+       struct completion       complete;
 #ifdef CONFIG_MTD_PARTITIONS
        struct mtd_partition *parts;
 #endif
@@ -191,7 +204,7 @@ static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val)
 
 static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val)
 {
-       return (type << S5PC1XX_CMD_MAP_SHIFT) | val;
+       return (type << S5PC100_CMD_MAP_SHIFT) | val;
 }
 
 static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa)
@@ -531,10 +544,13 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
        return 0;
 }
 
-static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
+static int (*s5pc110_dma_ops)(void *dst, void *src, size_t count, int direction);
+
+static int s5pc110_dma_poll(void *dst, void *src, size_t count, int direction)
 {
        void __iomem *base = onenand->dma_addr;
        int status;
+       unsigned long timeout;
 
        writel(src, base + S5PC110_DMA_SRC_ADDR);
        writel(dst, base + S5PC110_DMA_DST_ADDR);
@@ -552,6 +568,13 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
 
        writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
 
+       /*
+        * There's no exact timeout values at Spec.
+        * In real case it takes under 1 msec.
+        * So 20 msecs are enough.
+        */
+       timeout = jiffies + msecs_to_jiffies(20);
+
        do {
                status = readl(base + S5PC110_DMA_TRANS_STATUS);
                if (status & S5PC110_DMA_TRANS_STATUS_TE) {
@@ -559,13 +582,68 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
                                        base + S5PC110_DMA_TRANS_CMD);
                        return -EIO;
                }
-       } while (!(status & S5PC110_DMA_TRANS_STATUS_TD));
+       } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) &&
+               time_before(jiffies, timeout));
 
        writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
 
        return 0;
 }
 
+static irqreturn_t s5pc110_onenand_irq(int irq, void *data)
+{
+       void __iomem *base = onenand->dma_addr;
+       int status, cmd = 0;
+
+       status = readl(base + S5PC110_INTC_DMA_STATUS);
+
+       if (likely(status & S5PC110_INTC_DMA_TD))
+               cmd = S5PC110_DMA_TRANS_CMD_TDC;
+
+       if (unlikely(status & S5PC110_INTC_DMA_TE))
+               cmd = S5PC110_DMA_TRANS_CMD_TEC;
+
+       writel(cmd, base + S5PC110_DMA_TRANS_CMD);
+       writel(status, base + S5PC110_INTC_DMA_CLR);
+
+       if (!onenand->complete.done)
+               complete(&onenand->complete);
+
+       return IRQ_HANDLED;
+}
+
+static int s5pc110_dma_irq(void *dst, void *src, size_t count, int direction)
+{
+       void __iomem *base = onenand->dma_addr;
+       int status;
+
+       status = readl(base + S5PC110_INTC_DMA_MASK);
+       if (status) {
+               status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE);
+               writel(status, base + S5PC110_INTC_DMA_MASK);
+       }
+
+       writel(src, base + S5PC110_DMA_SRC_ADDR);
+       writel(dst, base + S5PC110_DMA_DST_ADDR);
+
+       if (direction == S5PC110_DMA_DIR_READ) {
+               writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG);
+               writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG);
+       } else {
+               writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG);
+               writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG);
+       }
+
+       writel(count, base + S5PC110_DMA_TRANS_SIZE);
+       writel(direction, base + S5PC110_DMA_TRANS_DIR);
+
+       writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
+
+       wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20));
+
+       return 0;
+}
+
 static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
                unsigned char *buffer, int offset, size_t count)
 {
@@ -573,7 +651,8 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
        void __iomem *p;
        void *buf = (void *) buffer;
        dma_addr_t dma_src, dma_dst;
-       int err;
+       int err, page_dma = 0;
+       struct device *dev = &onenand->pdev->dev;
 
        p = this->base + area;
        if (ONENAND_CURRENT_BUFFERRAM(this)) {
@@ -597,21 +676,27 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
                page = vmalloc_to_page(buf);
                if (!page)
                        goto normal;
-               buf = page_address(page) + ((size_t) buf & ~PAGE_MASK);
-       }
 
-       /* DMA routine */
-       dma_src = onenand->phys_base + (p - this->base);
-       dma_dst = dma_map_single(&onenand->pdev->dev,
-                       buf, count, DMA_FROM_DEVICE);
-       if (dma_mapping_error(&onenand->pdev->dev, dma_dst)) {
-               dev_err(&onenand->pdev->dev,
-                       "Couldn't map a %d byte buffer for DMA\n", count);
+               page_dma = 1;
+               /* DMA routine */
+               dma_src = onenand->phys_base + (p - this->base);
+               dma_dst = dma_map_page(dev, page, 0, count, DMA_FROM_DEVICE);
+       } else {
+               /* DMA routine */
+               dma_src = onenand->phys_base + (p - this->base);
+               dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE);
+       }
+       if (dma_mapping_error(dev, dma_dst)) {
+               dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count);
                goto normal;
        }
        err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src,
                        count, S5PC110_DMA_DIR_READ);
-       dma_unmap_single(&onenand->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);
+
+       if (page_dma)
+               dma_unmap_page(dev, dma_dst, count, DMA_FROM_DEVICE);
+       else
+               dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE);
 
        if (!err)
                return 0;
@@ -759,7 +844,6 @@ static void s3c_onenand_setup(struct mtd_info *mtd)
                onenand->cmd_map = s5pc1xx_cmd_map;
        } else if (onenand->type == TYPE_S5PC110) {
                /* Use generic onenand functions */
-               onenand->cmd_map = s5pc1xx_cmd_map;
                this->read_bufferram = s5pc110_read_bufferram;
                this->chip_probe = s5pc110_chip_probe;
                return;
@@ -904,6 +988,20 @@ static int s3c_onenand_probe(struct platform_device *pdev)
                }
 
                onenand->phys_base = onenand->base_res->start;
+
+               s5pc110_dma_ops = s5pc110_dma_poll;
+               /* Interrupt support */
+               r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+               if (r) {
+                       init_completion(&onenand->complete);
+                       s5pc110_dma_ops = s5pc110_dma_irq;
+                       err = request_irq(r->start, s5pc110_onenand_irq,
+                                       IRQF_SHARED, "onenand", &onenand);
+                       if (err) {
+                               dev_err(&pdev->dev, "failed to get irq\n");
+                               goto scan_failed;
+                       }
+               }
        }
 
        if (onenand_scan(mtd, 1)) {
@@ -1000,7 +1098,7 @@ static int s3c_pm_ops_suspend(struct device *dev)
        struct onenand_chip *this = mtd->priv;
 
        this->wait(mtd, FL_PM_SUSPENDED);
-       return mtd->suspend(mtd);
+       return 0;
 }
 
 static  int s3c_pm_ops_resume(struct device *dev)
@@ -1009,7 +1107,6 @@ static  int s3c_pm_ops_resume(struct device *dev)
        struct mtd_info *mtd = platform_get_drvdata(pdev);
        struct onenand_chip *this = mtd->priv;
 
-       mtd->resume(mtd);
        this->unlock_all(mtd);
        return 0;
 }
index e30e48e..43bb730 100644 (file)
@@ -20,7 +20,7 @@
 
 
 struct ftl_zone {
-       int initialized;
+       bool initialized;
        int16_t *lba_to_phys_table;             /* LBA to physical table */
        struct kfifo free_sectors;      /* queue of free sectors */
 };
@@ -37,8 +37,8 @@ struct sm_ftl {
        int zone_count;                 /* number of zones */
        int max_lba;                    /* maximum lba in a zone */
        int smallpagenand;              /* 256 bytes/page nand */
-       int readonly;                   /* is FS readonly */
-       int unstable;
+       bool readonly;                  /* is FS readonly */
+       bool unstable;
        int cis_block;                  /* CIS block location */
        int cis_boffset;                /* CIS offset in the block */
        int cis_page_offset;            /* CIS offset in the page */
@@ -49,7 +49,7 @@ struct sm_ftl {
        int cache_zone;                 /* zone of cached block */
        unsigned char *cache_data;      /* cached block data */
        long unsigned int cache_data_invalid_bitmap;
-       int cache_clean;
+       bool cache_clean;
        struct work_struct flush_work;
        struct timer_list timer;
 
index a906f53..85c6be2 100644 (file)
@@ -23,7 +23,7 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *,
 static inline struct jffs2_inode_cache *
 first_inode_chain(int *i, struct jffs2_sb_info *c)
 {
-       for (; *i < INOCACHE_HASHSIZE; (*i)++) {
+       for (; *i < c->inocache_hashsize; (*i)++) {
                if (c->inocache_list[*i])
                        return c->inocache_list[*i];
        }
index 617a1e5..de42470 100644 (file)
@@ -103,7 +103,7 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                        spin_unlock(&jffs2_compressor_list_lock);
                        *datalen  = orig_slen;
                        *cdatalen = orig_dlen;
-                       compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+                       compr_ret = this->compress(data_in, output_buf, datalen, cdatalen);
                        spin_lock(&jffs2_compressor_list_lock);
                        this->usecount--;
                        if (!compr_ret) {
@@ -152,7 +152,7 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                        spin_unlock(&jffs2_compressor_list_lock);
                        *datalen  = orig_slen;
                        *cdatalen = orig_dlen;
-                       compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+                       compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen);
                        spin_lock(&jffs2_compressor_list_lock);
                        this->usecount--;
                        if (!compr_ret) {
@@ -220,7 +220,7 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                        if (comprtype == this->compr) {
                                this->usecount++;
                                spin_unlock(&jffs2_compressor_list_lock);
-                               ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
+                               ret = this->decompress(cdata_in, data_out, cdatalen, datalen);
                                spin_lock(&jffs2_compressor_list_lock);
                                if (ret) {
                                        printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
index e471a91..13bb759 100644 (file)
@@ -49,9 +49,9 @@ struct jffs2_compressor {
        char *name;
        char compr;                     /* JFFS2_COMPR_XXX */
        int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
-                       uint32_t *srclen, uint32_t *destlen, void *model);
+                       uint32_t *srclen, uint32_t *destlen);
        int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
-                         uint32_t cdatalen, uint32_t datalen, void *model);
+                         uint32_t cdatalen, uint32_t datalen);
        int usecount;
        int disabled;           /* if set the compressor won't compress */
        unsigned char *compr_buf;       /* used by size compr. mode */
index ed25ae7..af186ee 100644 (file)
@@ -42,7 +42,7 @@ static int __init alloc_workspace(void)
 }
 
 static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
-                             uint32_t *sourcelen, uint32_t *dstlen, void *model)
+                             uint32_t *sourcelen, uint32_t *dstlen)
 {
        size_t compress_size;
        int ret;
@@ -67,7 +67,7 @@ static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
 }
 
 static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
-                                uint32_t srclen, uint32_t destlen, void *model)
+                                uint32_t srclen, uint32_t destlen)
 {
        size_t dl = destlen;
        int ret;
index 9696ad9..16a5047 100644 (file)
@@ -31,8 +31,7 @@
 /* _compress returns the compressed size, -1 if bigger */
 static int jffs2_rtime_compress(unsigned char *data_in,
                                unsigned char *cpage_out,
-                               uint32_t *sourcelen, uint32_t *dstlen,
-                               void *model)
+                               uint32_t *sourcelen, uint32_t *dstlen)
 {
        short positions[256];
        int outpos = 0;
@@ -73,8 +72,7 @@ static int jffs2_rtime_compress(unsigned char *data_in,
 
 static int jffs2_rtime_decompress(unsigned char *data_in,
                                  unsigned char *cpage_out,
-                                 uint32_t srclen, uint32_t destlen,
-                                 void *model)
+                                 uint32_t srclen, uint32_t destlen)
 {
        short positions[256];
        int outpos = 0;
index a12b4f7..9e7cec8 100644 (file)
@@ -298,7 +298,7 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
 #if 0
 /* _compress returns the compressed size, -1 if bigger */
 int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
-                  uint32_t *sourcelen, uint32_t *dstlen, void *model)
+                  uint32_t *sourcelen, uint32_t *dstlen)
 {
        return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in,
                                 cpage_out, sourcelen, dstlen);
@@ -306,8 +306,7 @@ int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
 #endif
 static int jffs2_dynrubin_compress(unsigned char *data_in,
                                   unsigned char *cpage_out,
-                                  uint32_t *sourcelen, uint32_t *dstlen,
-                                  void *model)
+                                  uint32_t *sourcelen, uint32_t *dstlen)
 {
        int bits[8];
        unsigned char histo[256];
@@ -387,8 +386,7 @@ static void rubin_do_decompress(int bit_divider, int *bits,
 
 static int jffs2_rubinmips_decompress(unsigned char *data_in,
                                      unsigned char *cpage_out,
-                                     uint32_t sourcelen, uint32_t dstlen,
-                                     void *model)
+                                     uint32_t sourcelen, uint32_t dstlen)
 {
        rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in,
                            cpage_out, sourcelen, dstlen);
@@ -397,8 +395,7 @@ static int jffs2_rubinmips_decompress(unsigned char *data_in,
 
 static int jffs2_dynrubin_decompress(unsigned char *data_in,
                                     unsigned char *cpage_out,
-                                    uint32_t sourcelen, uint32_t dstlen,
-                                    void *model)
+                                    uint32_t sourcelen, uint32_t dstlen)
 {
        int bits[8];
        int c;
index 97fc45d..fd05a0b 100644 (file)
@@ -68,8 +68,7 @@ static void free_workspaces(void)
 
 static int jffs2_zlib_compress(unsigned char *data_in,
                               unsigned char *cpage_out,
-                              uint32_t *sourcelen, uint32_t *dstlen,
-                              void *model)
+                              uint32_t *sourcelen, uint32_t *dstlen)
 {
        int ret;
 
@@ -136,8 +135,7 @@ static int jffs2_zlib_compress(unsigned char *data_in,
 
 static int jffs2_zlib_decompress(unsigned char *data_in,
                                 unsigned char *cpage_out,
-                                uint32_t srclen, uint32_t destlen,
-                                void *model)
+                                uint32_t srclen, uint32_t destlen)
 {
        int ret;
        int wbits = MAX_WBITS;
index ed78a3c..dd745c3 100644 (file)
@@ -367,7 +367,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
        }
 
        /* We use f->target field to store the target path. */
-       f->target = kmalloc(targetlen + 1, GFP_KERNEL);
+       f->target = kmemdup(target, targetlen + 1, GFP_KERNEL);
        if (!f->target) {
                printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
                mutex_unlock(&f->sem);
@@ -376,7 +376,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
                goto fail;
        }
 
-       memcpy(f->target, target, targetlen + 1);
        D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target));
 
        /* No data here. Only a metadata node, which will be
index abac961..e513f19 100644 (file)
@@ -151,7 +151,7 @@ int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
                }
 
                /* Be nice */
-               yield();
+               cond_resched();
                mutex_lock(&c->erase_free_sem);
                spin_lock(&c->erase_completion_lock);
        }
index d9beb06..e896e67 100644 (file)
@@ -474,6 +474,25 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i
        return inode;
 }
 
+static int calculate_inocache_hashsize(uint32_t flash_size)
+{
+       /*
+        * Pick a inocache hash size based on the size of the medium.
+        * Count how many megabytes we're dealing with, apply a hashsize twice
+        * that size, but rounding down to the usual big powers of 2. And keep
+        * to sensible bounds.
+        */
+
+       int size_mb = flash_size / 1024 / 1024;
+       int hashsize = (size_mb * 2) & ~0x3f;
+
+       if (hashsize < INOCACHE_HASHSIZE_MIN)
+               return INOCACHE_HASHSIZE_MIN;
+       if (hashsize > INOCACHE_HASHSIZE_MAX)
+               return INOCACHE_HASHSIZE_MAX;
+
+       return hashsize;
+}
 
 int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
 {
@@ -520,7 +539,8 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
        if (ret)
                return ret;
 
-       c->inocache_list = kcalloc(INOCACHE_HASHSIZE, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
+       c->inocache_hashsize = calculate_inocache_hashsize(c->flash_size);
+       c->inocache_list = kcalloc(c->inocache_hashsize, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
        if (!c->inocache_list) {
                ret = -ENOMEM;
                goto out_wbuf;
index 846a794..31dce61 100644 (file)
@@ -219,13 +219,14 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
        if (!list_empty(&c->erase_complete_list) ||
            !list_empty(&c->erase_pending_list)) {
                spin_unlock(&c->erase_completion_lock);
+               mutex_unlock(&c->alloc_sem);
                D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() erasing pending blocks\n"));
-               if (jffs2_erase_pending_blocks(c, 1)) {
-                       mutex_unlock(&c->alloc_sem);
+               if (jffs2_erase_pending_blocks(c, 1))
                        return 0;
-               }
+
                D1(printk(KERN_DEBUG "No progress from erasing blocks; doing GC anyway\n"));
                spin_lock(&c->erase_completion_lock);
+               mutex_lock(&c->alloc_sem);
        }
 
        /* First, work out which block we're garbage-collecting */
index 6784bc8..f864005 100644 (file)
@@ -100,6 +100,7 @@ struct jffs2_sb_info {
        wait_queue_head_t erase_wait;           /* For waiting for erases to complete */
 
        wait_queue_head_t inocache_wq;
+       int inocache_hashsize;
        struct jffs2_inode_cache **inocache_list;
        spinlock_t inocache_lock;
 
index af02bd1..5e03233 100644 (file)
@@ -420,7 +420,7 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t
 {
        struct jffs2_inode_cache *ret;
 
-       ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
+       ret = c->inocache_list[ino % c->inocache_hashsize];
        while (ret && ret->ino < ino) {
                ret = ret->next;
        }
@@ -441,7 +441,7 @@ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new
 
        dbg_inocache("add %p (ino #%u)\n", new, new->ino);
 
-       prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
+       prev = &c->inocache_list[new->ino % c->inocache_hashsize];
 
        while ((*prev) && (*prev)->ino < new->ino) {
                prev = &(*prev)->next;
@@ -462,7 +462,7 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
        dbg_inocache("del %p (ino #%u)\n", old, old->ino);
        spin_lock(&c->inocache_lock);
 
-       prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
+       prev = &c->inocache_list[old->ino % c->inocache_hashsize];
 
        while ((*prev) && (*prev)->ino < old->ino) {
                prev = &(*prev)->next;
@@ -487,7 +487,7 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
        int i;
        struct jffs2_inode_cache *this, *next;
 
-       for (i=0; i<INOCACHE_HASHSIZE; i++) {
+       for (i=0; i < c->inocache_hashsize; i++) {
                this = c->inocache_list[i];
                while (this) {
                        next = this->next;
index 523a916..5a53d9b 100644 (file)
@@ -199,7 +199,8 @@ struct jffs2_inode_cache {
 #define RAWNODE_CLASS_XATTR_DATUM      1
 #define RAWNODE_CLASS_XATTR_REF                2
 
-#define INOCACHE_HASHSIZE 128
+#define INOCACHE_HASHSIZE_MIN 128
+#define INOCACHE_HASHSIZE_MAX 1024
 
 #define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size)
 
index 46f870d..b632ddd 100644 (file)
@@ -20,7 +20,7 @@
 #include "summary.h"
 #include "debug.h"
 
-#define DEFAULT_EMPTY_SCAN_SIZE 1024
+#define DEFAULT_EMPTY_SCAN_SIZE 256
 
 #define noisy_printk(noise, args...) do { \
        if (*(noise)) { \
@@ -435,7 +435,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
                                  unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
        struct jffs2_unknown_node *node;
        struct jffs2_unknown_node crcnode;
-       uint32_t ofs, prevofs;
+       uint32_t ofs, prevofs, max_ofs;
        uint32_t hdr_crc, buf_ofs, buf_len;
        int err;
        int noise = 0;
@@ -550,12 +550,12 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
 
        /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
        ofs = 0;
-
-       /* Scan only 4KiB of 0xFF before declaring it's empty */
-       while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
+       max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
+       /* Scan only EMPTY_SCAN_SIZE of 0xFF before declaring it's empty */
+       while(ofs < max_ofs && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
                ofs += 4;
 
-       if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
+       if (ofs == max_ofs) {
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
                if (jffs2_cleanmarker_oob(c)) {
                        /* scan oob, take care of cleanmarker */
index 7fa20be..57cc0e6 100644 (file)
@@ -84,7 +84,7 @@ struct nand_bbt_descr {
 #define NAND_BBT_PERCHIP       0x00000080
 /* bbt has a version counter at offset veroffs */
 #define NAND_BBT_VERSION       0x00000100
-/* Create a bbt if none axists */
+/* Create a bbt if none exists */
 #define NAND_BBT_CREATE                0x00000200
 /* Search good / bad pattern through all pages of a block */
 #define NAND_BBT_SCANALLPAGES  0x00000400
@@ -102,6 +102,8 @@ struct nand_bbt_descr {
 #define NAND_BBT_SCANBYTE1AND6 0x00100000
 /* The nand_bbt_descr was created dynamicaly and must be freed */
 #define NAND_BBT_DYNAMICSTRUCT 0x00200000
+/* The bad block table does not OOB for marker */
+#define NAND_BBT_NO_OOB                0x00400000
 
 /* The maximum number of blocks to scan for a bbt */
 #define NAND_BBT_SCAN_MAXBLOCKS        4
index d2118b0..4dd0c2c 100644 (file)
@@ -289,6 +289,7 @@ struct cfi_private {
                                  must be of the same type. */
        int mfr, id;
        int numchips;
+       map_word sector_erase_cmd;
        unsigned long chipshift; /* Because they're of the same type */
        const char *im_name;     /* inter_module name for cmdset_setup */
        struct flchip chips[0];  /* per-chip data structure for each chip */
diff --git a/include/linux/mtd/fsmc.h b/include/linux/mtd/fsmc.h
new file mode 100644 (file)
index 0000000..5d25567
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * incude/mtd/fsmc.h
+ *
+ * ST Microelectronics
+ * Flexible Static Memory Controller (FSMC)
+ * platform data interface and header file
+ *
+ * Copyright Â© 2010 ST Microelectronics
+ * Vipin Kumar <vipin.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MTD_FSMC_H
+#define __MTD_FSMC_H
+
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+#include <linux/types.h>
+#include <linux/mtd/partitions.h>
+#include <asm/param.h>
+
+#define FSMC_NAND_BW8          1
+#define FSMC_NAND_BW16         2
+
+/*
+ * The placement of the Command Latch Enable (CLE) and
+ * Address Latch Enable (ALE) is twised around in the
+ * SPEAR310 implementation.
+ */
+#if defined(CONFIG_MACH_SPEAR310)
+#define PLAT_NAND_CLE          (1 << 17)
+#define PLAT_NAND_ALE          (1 << 16)
+#else
+#define PLAT_NAND_CLE          (1 << 16)
+#define PLAT_NAND_ALE          (1 << 17)
+#endif
+
+#define FSMC_MAX_NOR_BANKS     4
+#define FSMC_MAX_NAND_BANKS    4
+
+#define FSMC_FLASH_WIDTH8      1
+#define FSMC_FLASH_WIDTH16     2
+
+struct fsmc_nor_bank_regs {
+       uint32_t ctrl;
+       uint32_t ctrl_tim;
+};
+
+/* ctrl register definitions */
+#define BANK_ENABLE            (1 << 0)
+#define MUXED                  (1 << 1)
+#define NOR_DEV                        (2 << 2)
+#define WIDTH_8                        (0 << 4)
+#define WIDTH_16               (1 << 4)
+#define RSTPWRDWN              (1 << 6)
+#define WPROT                  (1 << 7)
+#define WRT_ENABLE             (1 << 12)
+#define WAIT_ENB               (1 << 13)
+
+/* ctrl_tim register definitions */
+
+struct fsms_nand_bank_regs {
+       uint32_t pc;
+       uint32_t sts;
+       uint32_t comm;
+       uint32_t attrib;
+       uint32_t ioata;
+       uint32_t ecc1;
+       uint32_t ecc2;
+       uint32_t ecc3;
+};
+
+#define FSMC_NOR_REG_SIZE      0x40
+
+struct fsmc_regs {
+       struct fsmc_nor_bank_regs nor_bank_regs[FSMC_MAX_NOR_BANKS];
+       uint8_t reserved_1[0x40 - 0x20];
+       struct fsms_nand_bank_regs bank_regs[FSMC_MAX_NAND_BANKS];
+       uint8_t reserved_2[0xfe0 - 0xc0];
+       uint32_t peripid0;                      /* 0xfe0 */
+       uint32_t peripid1;                      /* 0xfe4 */
+       uint32_t peripid2;                      /* 0xfe8 */
+       uint32_t peripid3;                      /* 0xfec */
+       uint32_t pcellid0;                      /* 0xff0 */
+       uint32_t pcellid1;                      /* 0xff4 */
+       uint32_t pcellid2;                      /* 0xff8 */
+       uint32_t pcellid3;                      /* 0xffc */
+};
+
+#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
+
+/* pc register definitions */
+#define FSMC_RESET             (1 << 0)
+#define FSMC_WAITON            (1 << 1)
+#define FSMC_ENABLE            (1 << 2)
+#define FSMC_DEVTYPE_NAND      (1 << 3)
+#define FSMC_DEVWID_8          (0 << 4)
+#define FSMC_DEVWID_16         (1 << 4)
+#define FSMC_ECCEN             (1 << 6)
+#define FSMC_ECCPLEN_512       (0 << 7)
+#define FSMC_ECCPLEN_256       (1 << 7)
+#define FSMC_TCLR_1            (1 << 9)
+#define FSMC_TAR_1             (1 << 13)
+
+/* sts register definitions */
+#define FSMC_CODE_RDY          (1 << 15)
+
+/* comm register definitions */
+#define FSMC_TSET_0            (0 << 0)
+#define FSMC_TWAIT_6           (6 << 8)
+#define FSMC_THOLD_4           (4 << 16)
+#define FSMC_THIZ_1            (1 << 24)
+
+/* peripid2 register definitions */
+#define FSMC_REVISION_MSK      (0xf)
+#define FSMC_REVISION_SHFT     (0x4)
+
+#define FSMC_VER1              1
+#define FSMC_VER2              2
+#define FSMC_VER3              3
+#define FSMC_VER4              4
+#define FSMC_VER5              5
+#define FSMC_VER6              6
+#define FSMC_VER7              7
+#define FSMC_VER8              8
+
+static inline uint32_t get_fsmc_version(struct fsmc_regs *regs)
+{
+       return (readl(&regs->peripid2) >> FSMC_REVISION_SHFT) &
+                               FSMC_REVISION_MSK;
+}
+
+/*
+ * There are 13 bytes of ecc for every 512 byte block in FSMC version 8
+ * and it has to be read consecutively and immediately after the 512
+ * byte data block for hardware to generate the error bit offsets
+ * Managing the ecc bytes in the following way is easier. This way is
+ * similar to oobfree structure maintained already in u-boot nand driver
+ */
+#define MAX_ECCPLACE_ENTRIES   32
+
+struct fsmc_nand_eccplace {
+       uint8_t offset;
+       uint8_t length;
+};
+
+struct fsmc_eccplace {
+       struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES];
+};
+
+/**
+ * fsmc_nand_platform_data - platform specific NAND controller config
+ * @partitions: partition table for the platform, use a default fallback
+ * if this is NULL
+ * @nr_partitions: the number of partitions in the previous entry
+ * @options: different options for the driver
+ * @width: bus width
+ * @bank: default bank
+ * @select_bank: callback to select a certain bank, this is
+ * platform-specific. If the controller only supports one bank
+ * this may be set to NULL
+ */
+struct fsmc_nand_platform_data {
+       struct mtd_partition    *partitions;
+       unsigned int            nr_partitions;
+       unsigned int            options;
+       unsigned int            width;
+       unsigned int            bank;
+       void                    (*select_bank)(uint32_t bank, uint32_t busw);
+};
+
+extern int __init fsmc_nor_init(struct platform_device *pdev,
+               unsigned long base, uint32_t bank, uint32_t width);
+extern void __init fsmc_init_board_info(struct platform_device *pdev,
+               struct mtd_partition *partitions, unsigned int nr_partitions,
+               unsigned int width);
+
+#endif /* __MTD_FSMC_H */
index 64ee53c..02cd5f9 100644 (file)
@@ -37,14 +37,14 @@ struct INFTLrecord {
        __u16 firstEUN;
        __u16 lastEUN;
        __u16 numfreeEUNs;
-       __u16 LastFreeEUN;              /* To speed up finding a free EUN */
+       __u16 LastFreeEUN;              /* To speed up finding a free EUN */
        int head,sect,cyl;
-       __u16 *PUtable;                 /* Physical Unit Table  */
-       __u16 *VUtable;                 /* Virtual Unit Table */
-        unsigned int nb_blocks;                /* number of physical blocks */
-        unsigned int nb_boot_blocks;   /* number of blocks used by the bios */
-        struct erase_info instr;
-        struct nand_ecclayout oobinfo;
+       __u16 *PUtable;                 /* Physical Unit Table */
+       __u16 *VUtable;                 /* Virtual Unit Table */
+       unsigned int nb_blocks;         /* number of physical blocks */
+       unsigned int nb_boot_blocks;    /* number of blocks used by the bios */
+       struct erase_info instr;
+       struct nand_ecclayout oobinfo;
 };
 
 int INFTL_mount(struct INFTLrecord *s);
index 8485e42..fe8d77e 100644 (file)
@@ -110,6 +110,21 @@ struct mtd_oob_ops {
        uint8_t         *oobbuf;
 };
 
+#define MTD_MAX_OOBFREE_ENTRIES_LARGE  32
+#define MTD_MAX_ECCPOS_ENTRIES_LARGE   448
+/*
+ * Internal ECC layout control structure. For historical reasons, there is a
+ * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
+ * for export to user-space via the ECCGETLAYOUT ioctl.
+ * nand_ecclayout should be expandable in the future simply by the above macros.
+ */
+struct nand_ecclayout {
+       __u32 eccbytes;
+       __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
+       __u32 oobavail;
+       struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
+};
+
 struct mtd_info {
        u_char type;
        uint32_t flags;
index 102e12c..63e17d0 100644 (file)
 struct mtd_info;
 struct nand_flash_dev;
 /* Scan and identify a NAND device */
-extern int nand_scan (struct mtd_info *mtd, int max_chips);
-/* Separate phases of nand_scan(), allowing board driver to intervene
- * and override command or ECC setup according to flash type */
+extern int nand_scan(struct mtd_info *mtd, int max_chips);
+/*
+ * Separate phases of nand_scan(), allowing board driver to intervene
+ * and override command or ECC setup according to flash type.
+ */
 extern int nand_scan_ident(struct mtd_info *mtd, int max_chips,
                           struct nand_flash_dev *table);
 extern int nand_scan_tail(struct mtd_info *mtd);
 
 /* Free resources held by the NAND device */
-extern void nand_release (struct mtd_info *mtd);
+extern void nand_release(struct mtd_info *mtd);
 
 /* Internal helper for board drivers which need to override command function */
 extern void nand_wait_ready(struct mtd_info *mtd);
@@ -49,12 +51,13 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 /* The maximum number of NAND chips in an array */
 #define NAND_MAX_CHIPS         8
 
-/* This constant declares the max. oobsize / page, which
+/*
+ * This constant declares the max. oobsize / page, which
  * is supported now. If you add a chip with bigger oobsize/page
  * adjust this accordingly.
  */
-#define NAND_MAX_OOBSIZE       256
-#define NAND_MAX_PAGESIZE      4096
+#define NAND_MAX_OOBSIZE       576
+#define NAND_MAX_PAGESIZE      8192
 
 /*
  * Constants for hardware specific CLE/ALE/NCE function
@@ -88,6 +91,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 #define NAND_CMD_RNDIN         0x85
 #define NAND_CMD_READID                0x90
 #define NAND_CMD_ERASE2                0xd0
+#define NAND_CMD_PARAM         0xec
 #define NAND_CMD_RESET         0xff
 
 #define NAND_CMD_LOCK          0x2a
@@ -152,9 +156,10 @@ typedef enum {
 #define NAND_GET_DEVICE                0x80
 
 
-/* Option constants for bizarre disfunctionality and real
-*  features
-*/
+/*
+ * Option constants for bizarre disfunctionality and real
+ * features.
+ */
 /* Chip can not auto increment pages */
 #define NAND_NO_AUTOINCR       0x00000001
 /* Buswitdh is 16 bit */
@@ -165,19 +170,27 @@ typedef enum {
 #define NAND_CACHEPRG          0x00000008
 /* Chip has copy back function */
 #define NAND_COPYBACK          0x00000010
-/* AND Chip which has 4 banks and a confusing page / block
- * assignment. See Renesas datasheet for further information */
+/*
+ * AND Chip which has 4 banks and a confusing page / block
+ * assignment. See Renesas datasheet for further information.
+ */
 #define NAND_IS_AND            0x00000020
-/* Chip has a array of 4 pages which can be read without
- * additional ready /busy waits */
+/*
+ * Chip has a array of 4 pages which can be read without
+ * additional ready /busy waits.
+ */
 #define NAND_4PAGE_ARRAY       0x00000040
-/* Chip requires that BBT is periodically rewritten to prevent
+/*
+ * Chip requires that BBT is periodically rewritten to prevent
  * bits from adjacent blocks from 'leaking' in altering data.
- * This happens with the Renesas AG-AND chips, possibly others.  */
+ * This happens with the Renesas AG-AND chips, possibly others.
+ */
 #define BBT_AUTO_REFRESH       0x00000080
-/* Chip does not require ready check on read. True
+/*
+ * Chip does not require ready check on read. True
  * for all large page devices, as they do not support
- * autoincrement.*/
+ * autoincrement.
+ */
 #define NAND_NO_READRDY                0x00000100
 /* Chip does not allow subpage writes */
 #define NAND_NO_SUBPAGE_WRITE  0x00000200
@@ -205,16 +218,27 @@ typedef enum {
 #define NAND_CHIPOPTIONS_MSK   (0x0000ffff & ~NAND_NO_AUTOINCR)
 
 /* Non chip related options */
-/* Use a flash based bad block table. This option is passed to the
- * default bad block table function. */
+/*
+ * Use a flash based bad block table. OOB identifier is saved in OOB area.
+ * This option is passed to the default bad block table function.
+ */
 #define NAND_USE_FLASH_BBT     0x00010000
 /* This option skips the bbt scan during initialization. */
 #define NAND_SKIP_BBTSCAN      0x00020000
-/* This option is defined if the board driver allocates its own buffers
-   (e.g. because it needs them DMA-coherent */
+/*
+ * This option is defined if the board driver allocates its own buffers
+ * (e.g. because it needs them DMA-coherent).
+ */
 #define NAND_OWN_BUFFERS       0x00040000
 /* Chip may not exist, so silence any errors in scan */
 #define NAND_SCAN_SILENT_NODEV 0x00080000
+/*
+ * If passed additionally to NAND_USE_FLASH_BBT then BBT code will not touch
+ * the OOB area.
+ */
+#define NAND_USE_FLASH_BBT_NO_OOB      0x00100000
+/* Create an empty BBT with no vendor information if the BBT is available */
+#define NAND_CREATE_EMPTY_BBT          0x00200000
 
 /* Options set by nand scan */
 /* Nand scan has allocated controller struct */
@@ -227,15 +251,80 @@ typedef enum {
 /* Keep gcc happy */
 struct nand_chip;
 
+struct nand_onfi_params {
+       /* rev info and features block */
+       /* 'O' 'N' 'F' 'I'  */
+       u8 sig[4];
+       __le16 revision;
+       __le16 features;
+       __le16 opt_cmd;
+       u8 reserved[22];
+
+       /* manufacturer information block */
+       char manufacturer[12];
+       char model[20];
+       u8 jedec_id;
+       __le16 date_code;
+       u8 reserved2[13];
+
+       /* memory organization block */
+       __le32 byte_per_page;
+       __le16 spare_bytes_per_page;
+       __le32 data_bytes_per_ppage;
+       __le16 spare_bytes_per_ppage;
+       __le32 pages_per_block;
+       __le32 blocks_per_lun;
+       u8 lun_count;
+       u8 addr_cycles;
+       u8 bits_per_cell;
+       __le16 bb_per_lun;
+       __le16 block_endurance;
+       u8 guaranteed_good_blocks;
+       __le16 guaranteed_block_endurance;
+       u8 programs_per_page;
+       u8 ppage_attr;
+       u8 ecc_bits;
+       u8 interleaved_bits;
+       u8 interleaved_ops;
+       u8 reserved3[13];
+
+       /* electrical parameter block */
+       u8 io_pin_capacitance_max;
+       __le16 async_timing_mode;
+       __le16 program_cache_timing_mode;
+       __le16 t_prog;
+       __le16 t_bers;
+       __le16 t_r;
+       __le16 t_ccs;
+       __le16 src_sync_timing_mode;
+       __le16 src_ssync_features;
+       __le16 clk_pin_capacitance_typ;
+       __le16 io_pin_capacitance_typ;
+       __le16 input_pin_capacitance_typ;
+       u8 input_pin_capacitance_max;
+       u8 driver_strenght_support;
+       __le16 t_int_r;
+       __le16 t_ald;
+       u8 reserved4[7];
+
+       /* vendor */
+       u8 reserved5[90];
+
+       __le16 crc;
+} __attribute__((packed));
+
+#define ONFI_CRC_BASE  0x4F4E
+
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
  * @active:            the mtd device which holds the controller currently
- * @wq:                        wait queue to sleep on if a NAND operation is in progress
- *                      used instead of the per chip wait queue when a hw controller is available
+ * @wq:                        wait queue to sleep on if a NAND operation is in
+ *                     progress used instead of the per chip wait queue
+ *                     when a hw controller is available.
  */
 struct nand_hw_control {
-       spinlock_t       lock;
+       spinlock_t lock;
        struct nand_chip *active;
        wait_queue_head_t wq;
 };
@@ -256,51 +345,42 @@ struct nand_hw_control {
  * @correct:   function for ecc correction, matching to ecc generator (sw/hw)
  * @read_page_raw:     function to read a raw page without ECC
  * @write_page_raw:    function to write a raw page without ECC
- * @read_page: function to read a page according to the ecc generator requirements
+ * @read_page: function to read a page according to the ecc generator
+ *             requirements.
  * @read_subpage:      function to read parts of the page covered by ECC.
- * @write_page:        function to write a page according to the ecc generator requirements
+ * @write_page:        function to write a page according to the ecc generator
+ *             requirements.
  * @read_oob:  function to read chip OOB data
  * @write_oob: function to write chip OOB data
  */
 struct nand_ecc_ctrl {
-       nand_ecc_modes_t        mode;
-       int                     steps;
-       int                     size;
-       int                     bytes;
-       int                     total;
-       int                     prepad;
-       int                     postpad;
+       nand_ecc_modes_t mode;
+       int steps;
+       int size;
+       int bytes;
+       int total;
+       int prepad;
+       int postpad;
        struct nand_ecclayout   *layout;
-       void                    (*hwctl)(struct mtd_info *mtd, int mode);
-       int                     (*calculate)(struct mtd_info *mtd,
-                                            const uint8_t *dat,
-                                            uint8_t *ecc_code);
-       int                     (*correct)(struct mtd_info *mtd, uint8_t *dat,
-                                          uint8_t *read_ecc,
-                                          uint8_t *calc_ecc);
-       int                     (*read_page_raw)(struct mtd_info *mtd,
-                                                struct nand_chip *chip,
-                                                uint8_t *buf, int page);
-       void                    (*write_page_raw)(struct mtd_info *mtd,
-                                                 struct nand_chip *chip,
-                                                 const uint8_t *buf);
-       int                     (*read_page)(struct mtd_info *mtd,
-                                            struct nand_chip *chip,
-                                            uint8_t *buf, int page);
-       int                     (*read_subpage)(struct mtd_info *mtd,
-                                            struct nand_chip *chip,
-                                            uint32_t offs, uint32_t len,
-                                            uint8_t *buf);
-       void                    (*write_page)(struct mtd_info *mtd,
-                                             struct nand_chip *chip,
-                                             const uint8_t *buf);
-       int                     (*read_oob)(struct mtd_info *mtd,
-                                           struct nand_chip *chip,
-                                           int page,
-                                           int sndcmd);
-       int                     (*write_oob)(struct mtd_info *mtd,
-                                            struct nand_chip *chip,
-                                            int page);
+       void (*hwctl)(struct mtd_info *mtd, int mode);
+       int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
+                       uint8_t *ecc_code);
+       int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
+                       uint8_t *calc_ecc);
+       int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
+                       uint8_t *buf, int page);
+       void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
+                       const uint8_t *buf);
+       int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
+                       uint8_t *buf, int page);
+       int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
+                       uint32_t offs, uint32_t len, uint8_t *buf);
+       void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
+                       const uint8_t *buf);
+       int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page,
+                       int sndcmd);
+       int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
+                       int page);
 };
 
 /**
@@ -320,102 +400,132 @@ struct nand_buffers {
 
 /**
  * struct nand_chip - NAND Private Flash Chip Data
- * @IO_ADDR_R:         [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
- * @IO_ADDR_W:         [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
+ * @IO_ADDR_R:         [BOARDSPECIFIC] address to read the 8 I/O lines of the
+ *                     flash device
+ * @IO_ADDR_W:         [BOARDSPECIFIC] address to write the 8 I/O lines of the
+ *                     flash device.
  * @read_byte:         [REPLACEABLE] read one byte from the chip
  * @read_word:         [REPLACEABLE] read one word from the chip
  * @write_buf:         [REPLACEABLE] write data from the buffer to the chip
  * @read_buf:          [REPLACEABLE] read data from the chip into the buffer
- * @verify_buf:                [REPLACEABLE] verify buffer contents against the chip data
+ * @verify_buf:                [REPLACEABLE] verify buffer contents against the chip
+ *                     data.
  * @select_chip:       [REPLACEABLE] select chip nr
  * @block_bad:         [REPLACEABLE] check, if the block is bad
  * @block_markbad:     [REPLACEABLE] mark the block bad
  * @cmd_ctrl:          [BOARDSPECIFIC] hardwarespecific funtion for controlling
  *                     ALE/CLE/nCE. Also used to write command and address
- * @dev_ready:         [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
- *                     If set to NULL no access to ready/busy is available and the ready/busy information
- *                     is read from the chip status register
- * @cmdfunc:           [REPLACEABLE] hardwarespecific function for writing commands to the chip
- * @waitfunc:          [REPLACEABLE] hardwarespecific function for wait on ready
+ * @init_size:         [BOARDSPECIFIC] hardwarespecific funtion for setting
+ *                     mtd->oobsize, mtd->writesize and so on.
+ *                     @id_data contains the 8 bytes values of NAND_CMD_READID.
+ *                     Return with the bus width.
+ * @dev_ready:         [BOARDSPECIFIC] hardwarespecific function for accesing
+ *                     device ready/busy line. If set to NULL no access to
+ *                     ready/busy is available and the ready/busy information
+ *                     is read from the chip status register.
+ * @cmdfunc:           [REPLACEABLE] hardwarespecific function for writing
+ *                     commands to the chip.
+ * @waitfunc:          [REPLACEABLE] hardwarespecific function for wait on
+ *                     ready.
  * @ecc:               [BOARDSPECIFIC] ecc control ctructure
  * @buffers:           buffer structure for read/write
  * @hwcontrol:         platform-specific hardware control structure
  * @ops:               oob operation operands
- * @erase_cmd:         [INTERN] erase command write function, selectable due to AND support
+ * @erase_cmd:         [INTERN] erase command write function, selectable due
+ *                     to AND support.
  * @scan_bbt:          [REPLACEABLE] function to scan bad block table
- * @chip_delay:                [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
+ * @chip_delay:                [BOARDSPECIFIC] chip dependent delay for transfering
+ *                     data from array to read regs (tR).
  * @state:             [INTERN] the current state of the NAND device
  * @oob_poi:           poison value buffer
- * @page_shift:                [INTERN] number of address bits in a page (column address bits)
+ * @page_shift:                [INTERN] number of address bits in a page (column
+ *                     address bits).
  * @phys_erase_shift:  [INTERN] number of address bits in a physical eraseblock
  * @bbt_erase_shift:   [INTERN] number of address bits in a bbt entry
  * @chip_shift:                [INTERN] number of address bits in one chip
- * @options:           [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
- *                     special functionality. See the defines for further explanation
- * @badblockpos:       [INTERN] position of the bad block marker in the oob area
+ * @options:           [BOARDSPECIFIC] various chip options. They can partly
+ *                     be set to inform nand_scan about special functionality.
+ *                     See the defines for further explanation.
+ * @badblockpos:       [INTERN] position of the bad block marker in the oob
+ *                     area.
  * @cellinfo:          [INTERN] MLC/multichip data from chip ident
  * @numchips:          [INTERN] number of physical chips
  * @chipsize:          [INTERN] the size of one chip for multichip arrays
  * @pagemask:          [INTERN] page number mask = number of (pages / chip) - 1
- * @pagebuf:           [INTERN] holds the pagenumber which is currently in data_buf
+ * @pagebuf:           [INTERN] holds the pagenumber which is currently in
+ *                     data_buf.
  * @subpagesize:       [INTERN] holds the subpagesize
+ * @onfi_version:      [INTERN] holds the chip ONFI version (BCD encoded),
+ *                     non 0 if ONFI supported.
+ * @onfi_params:       [INTERN] holds the ONFI page parameter when ONFI is
+ *                     supported, 0 otherwise.
  * @ecclayout:         [REPLACEABLE] the default ecc placement scheme
  * @bbt:               [INTERN] bad block table pointer
- * @bbt_td:            [REPLACEABLE] bad block table descriptor for flash lookup
+ * @bbt_td:            [REPLACEABLE] bad block table descriptor for flash
+ *                     lookup.
  * @bbt_md:            [REPLACEABLE] bad block table mirror descriptor
- * @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial bad block scan
- * @controller:                [REPLACEABLE] a pointer to a hardware controller structure
- *                     which is shared among multiple independend devices
+ * @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial
+ *                     bad block scan.
+ * @controller:                [REPLACEABLE] a pointer to a hardware controller
+ *                     structure which is shared among multiple independend
+ *                     devices.
  * @priv:              [OPTIONAL] pointer to private chip date
- * @errstat:           [OPTIONAL] hardware specific function to perform additional error status checks
- *                     (determine if errors are correctable)
+ * @errstat:           [OPTIONAL] hardware specific function to perform
+ *                     additional error status checks (determine if errors are
+ *                     correctable).
  * @write_page:                [REPLACEABLE] High-level page write function
  */
 
 struct nand_chip {
-       void  __iomem   *IO_ADDR_R;
-       void  __iomem   *IO_ADDR_W;
-
-       uint8_t         (*read_byte)(struct mtd_info *mtd);
-       u16             (*read_word)(struct mtd_info *mtd);
-       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);
-       int             (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
-       void            (*select_chip)(struct mtd_info *mtd, int chip);
-       int             (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
-       int             (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
-       void            (*cmd_ctrl)(struct mtd_info *mtd, int dat,
-                                   unsigned int ctrl);
-       int             (*dev_ready)(struct mtd_info *mtd);
-       void            (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
-       int             (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
-       void            (*erase_cmd)(struct mtd_info *mtd, int page);
-       int             (*scan_bbt)(struct mtd_info *mtd);
-       int             (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
-       int             (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
-                                     const uint8_t *buf, int page, int cached, int raw);
-
-       int             chip_delay;
-       unsigned int    options;
-
-       int             page_shift;
-       int             phys_erase_shift;
-       int             bbt_erase_shift;
-       int             chip_shift;
-       int             numchips;
-       uint64_t        chipsize;
-       int             pagemask;
-       int             pagebuf;
-       int             subpagesize;
-       uint8_t         cellinfo;
-       int             badblockpos;
-       int             badblockbits;
-
-       flstate_t       state;
-
-       uint8_t         *oob_poi;
-       struct nand_hw_control  *controller;
-       struct nand_ecclayout   *ecclayout;
+       void __iomem *IO_ADDR_R;
+       void __iomem *IO_ADDR_W;
+
+       uint8_t (*read_byte)(struct mtd_info *mtd);
+       u16 (*read_word)(struct mtd_info *mtd);
+       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);
+       int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
+       void (*select_chip)(struct mtd_info *mtd, int chip);
+       int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
+       int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
+       void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
+       int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
+                       u8 *id_data);
+       int (*dev_ready)(struct mtd_info *mtd);
+       void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
+                       int page_addr);
+       int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
+       void (*erase_cmd)(struct mtd_info *mtd, int page);
+       int (*scan_bbt)(struct mtd_info *mtd);
+       int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
+                       int status, int page);
+       int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
+                       const uint8_t *buf, int page, int cached, int raw);
+
+       int chip_delay;
+       unsigned int options;
+
+       int page_shift;
+       int phys_erase_shift;
+       int bbt_erase_shift;
+       int chip_shift;
+       int numchips;
+       uint64_t chipsize;
+       int pagemask;
+       int pagebuf;
+       int subpagesize;
+       uint8_t cellinfo;
+       int badblockpos;
+       int badblockbits;
+
+       int onfi_version;
+       struct nand_onfi_params onfi_params;
+
+       flstate_t state;
+
+       uint8_t *oob_poi;
+       struct nand_hw_control *controller;
+       struct nand_ecclayout *ecclayout;
 
        struct nand_ecc_ctrl ecc;
        struct nand_buffers *buffers;
@@ -423,13 +533,13 @@ struct nand_chip {
 
        struct mtd_oob_ops ops;
 
-       uint8_t         *bbt;
-       struct nand_bbt_descr   *bbt_td;
-       struct nand_bbt_descr   *bbt_md;
+       uint8_t *bbt;
+       struct nand_bbt_descr *bbt_td;
+       struct nand_bbt_descr *bbt_md;
 
-       struct nand_bbt_descr   *badblock_pattern;
+       struct nand_bbt_descr *badblock_pattern;
 
-       void            *priv;
+       void *priv;
 };
 
 /*
@@ -473,7 +583,7 @@ struct nand_flash_dev {
 */
 struct nand_manufacturers {
        int id;
-       char * name;
+       char *name;
 };
 
 extern struct nand_flash_dev nand_flash_ids[];
@@ -486,7 +596,7 @@ extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
 extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                           int allowbbt);
 extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
-                       size_t * retlen, uint8_t * buf);
+                       size_t *retlen, uint8_t *buf);
 
 /**
  * struct platform_nand_chip - chip level device structure
@@ -502,17 +612,16 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
  * @priv:              hardware controller specific settings
  */
 struct platform_nand_chip {
-       int                     nr_chips;
-       int                     chip_offset;
-       int                     nr_partitions;
-       struct mtd_partition    *partitions;
-       struct nand_ecclayout   *ecclayout;
-       int                     chip_delay;
-       unsigned int            options;
-       const char              **part_probe_types;
-       void                    (*set_parts)(uint64_t size,
-                                       struct platform_nand_chip *chip);
-       void                    *priv;
+       int nr_chips;
+       int chip_offset;
+       int nr_partitions;
+       struct mtd_partition *partitions;
+       struct nand_ecclayout *ecclayout;
+       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 */
@@ -534,18 +643,15 @@ struct platform_device;
  * 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;
+       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;
 };
 
 /**
@@ -554,8 +660,8 @@ struct platform_nand_ctrl {
  * @ctrl:              controller level device structure
  */
 struct platform_nand_data {
-       struct platform_nand_chip       chip;
-       struct platform_nand_ctrl       ctrl;
+       struct platform_nand_chip chip;
+       struct platform_nand_ctrl ctrl;
 };
 
 /* Some helpers to access the data structures */
index 274b619..2b54316 100644 (file)
@@ -39,7 +39,7 @@ struct mtd_partition {
        uint64_t size;                  /* partition size */
        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 nand_ecclayout *ecclayout;       /* out of band layout for this partition (NAND only) */
 };
 
 #define MTDPART_OFS_NXTBLK     (-2)
@@ -89,4 +89,9 @@ static inline int mtd_has_cmdlinepart(void) { return 1; }
 static inline int mtd_has_cmdlinepart(void) { return 0; }
 #endif
 
+int mtd_is_master(struct mtd_info *mtd);
+int mtd_add_partition(struct mtd_info *master, char *name,
+                     long long offset, long long length);
+int mtd_del_partition(struct mtd_info *master, int partno);
+
 #endif
index 4debb45..2f7d45b 100644 (file)
@@ -52,6 +52,7 @@ struct mtd_oob_buf64 {
 #define MTD_NANDFLASH          4
 #define MTD_DATAFLASH          6
 #define MTD_UBIVOLUME          7
+#define MTD_MLCNANDFLASH       8
 
 #define MTD_WRITEABLE          0x400   /* Device is writeable */
 #define MTD_BIT_WRITEABLE      0x800   /* Single bits can be flipped */
@@ -119,7 +120,7 @@ struct otp_info {
 #define OTPGETREGIONCOUNT      _IOW('M', 14, int)
 #define OTPGETREGIONINFO       _IOW('M', 15, struct otp_info)
 #define OTPLOCK                        _IOR('M', 16, struct otp_info)
-#define ECCGETLAYOUT           _IOR('M', 17, struct nand_ecclayout)
+#define ECCGETLAYOUT           _IOR('M', 17, struct nand_ecclayout_user)
 #define ECCGETSTATS            _IOR('M', 18, struct mtd_ecc_stats)
 #define MTDFILEMODE            _IO('M', 19)
 #define MEMERASE64             _IOW('M', 20, struct erase_info_user64)
@@ -144,13 +145,18 @@ struct nand_oobfree {
 };
 
 #define MTD_MAX_OOBFREE_ENTRIES        8
+#define MTD_MAX_ECCPOS_ENTRIES 64
 /*
- * ECC layout control structure. Exported to userspace for
- * diagnosis and to allow creation of raw images
+ * OBSOLETE: ECC layout control structure. Exported to user-space via ioctl
+ * ECCGETLAYOUT for backwards compatbility and should not be mistaken as a
+ * complete set of ECC information. The ioctl truncates the larger internal
+ * structure to retain binary compatibility with the static declaration of the
+ * ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of
+ * the user struct, not the MAX size of the internal struct nand_ecclayout.
  */
-struct nand_ecclayout {
+struct nand_ecclayout_user {
        __u32 eccbytes;
-       __u32 eccpos[64];
+       __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES];
        __u32 oobavail;
        struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
 };
index aa3c2f8..83327c8 100644 (file)
@@ -29,6 +29,6 @@ typedef struct mtd_info_user mtd_info_t;
 typedef struct erase_info_user erase_info_t;
 typedef struct region_info_user region_info_t;
 typedef struct nand_oobinfo nand_oobinfo_t;
-typedef struct nand_ecclayout nand_ecclayout_t;
+typedef struct nand_ecclayout_user nand_ecclayout_t;
 
 #endif /* __MTD_USER_H__ */