]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/commitdiff
Merge git://git.infradead.org/mtd-2.6
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 19 Feb 2007 21:34:11 +0000 (13:34 -0800)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 19 Feb 2007 21:34:11 +0000 (13:34 -0800)
* git://git.infradead.org/mtd-2.6: (49 commits)
  [MTD] [NAND] S3C2412 fix hw ecc
  [MTD] [NAND] Work around false compiler warning in CAFÉ driver
  [JFFS2] printk warning fixes
  [MTD] [MAPS] ichxrom warning fix
  [MTD] [MAPS] amd76xrom warning fix
  [MTD] [MAPS] esb2rom warning fixes
  [MTD] [MAPS] ck804xrom warning fix
  [MTD] [MAPS] netsc520 warning fix
  [MTD] [MAPS] sc520cdp warning fix
  [MTD] [ONENAND] onenand_base warning fix
  [MTD] [NAND] eXcite nand flash driver
  [MTD] Improve heuristic for detecting wrong-endian RedBoot partition table
  [MTD] Fix RedBoot partition parsing regression harder.
  [MTD] [NAND] S3C2410: Hardware ECC correction code
  [JFFS2] Use MTD_OOB_AUTO to automatically place cleanmarker on NAND
  [MTD] Clarify OOB-operation interface comments
  [MTD] remove unused ecctype,eccsize fields from struct mtd_info
  [MTD] [NOR] Intel: remove ugly PROGREGION macros
  [MTD] [NOR] STAA: use writesize instead off eccsize to represent ECC block
  [MTD] OneNAND: Invalidate bufferRAM after erase
  ...

37 files changed:
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0020.c
drivers/mtd/devices/doc2000.c
drivers/mtd/devices/doc2001.c
drivers/mtd/devices/doc2001plus.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/amd76xrom.c
drivers/mtd/maps/ck804xrom.c
drivers/mtd/maps/esb2rom.c
drivers/mtd/maps/ichxrom.c
drivers/mtd/maps/netsc520.c
drivers/mtd/maps/sc520cdp.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/cafe.c
drivers/mtd/nand/cafe_ecc.c
drivers/mtd/nand/excite_nandflash.c [new file with mode: 0644]
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/onenand/onenand_base.c
drivers/mtd/onenand/onenand_bbt.c
drivers/mtd/redboot.c
fs/jffs2/build.c
fs/jffs2/jffs2_fs_sb.h
fs/jffs2/scan.c
fs/jffs2/wbuf.c
include/linux/mtd/bbm.h
include/linux/mtd/map.h
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
include/linux/mtd/onenand.h
include/linux/mtd/onenand_regs.h
include/linux/mtd/physmap.h
include/mtd/mtd-abi.h

index f69184a92eb2389d7370d726a346602c117d51dd..f334959a335ba0fc3bcb90f5042be352f871a14b 100644 (file)
@@ -397,9 +397,23 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
        cfi_fixup(mtd, fixup_table);
 
        for (i=0; i< cfi->numchips; i++) {
-               cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
-               cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
-               cfi->chips[i].erase_time = 1000<<cfi->cfiq->BlockEraseTimeoutTyp;
+               if (cfi->cfiq->WordWriteTimeoutTyp)
+                       cfi->chips[i].word_write_time =
+                               1<<cfi->cfiq->WordWriteTimeoutTyp;
+               else
+                       cfi->chips[i].word_write_time = 50000;
+
+               if (cfi->cfiq->BufWriteTimeoutTyp)
+                       cfi->chips[i].buffer_write_time =
+                               1<<cfi->cfiq->BufWriteTimeoutTyp;
+               /* No default; if it isn't specified, we won't use it */
+
+               if (cfi->cfiq->BlockEraseTimeoutTyp)
+                       cfi->chips[i].erase_time =
+                               1000<<cfi->cfiq->BlockEraseTimeoutTyp;
+               else
+                       cfi->chips[i].erase_time = 2000000;
+
                cfi->chips[i].ref_point_counter = 0;
                init_waitqueue_head(&(cfi->chips[i].wq));
        }
@@ -546,13 +560,11 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
                        struct cfi_intelext_programming_regioninfo *prinfo;
                        prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];
                        mtd->writesize = cfi->interleave << prinfo->ProgRegShift;
-                       MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid;
-                       MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid;
                        mtd->flags &= ~MTD_BIT_WRITEABLE;
                        printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",
                               map->name, mtd->writesize,
-                              MTD_PROGREGION_CTRLMODE_VALID(mtd),
-                              MTD_PROGREGION_CTRLMODE_INVALID(mtd));
+                              cfi->interleave * prinfo->ControlValid,
+                              cfi->interleave * prinfo->ControlInvalid);
                }
 
                /*
index d56849f5f107bbb119569ef42c27f3cdbc5e60ab..69d49e0250a9a3a532ff95f9ac7ce261862e314d 100644 (file)
@@ -662,7 +662,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to,
  * a small buffer for this.
  * XXX: If the buffer size is not a multiple of 2, this will break
  */
-#define ECCBUF_SIZE (mtd->eccsize)
+#define ECCBUF_SIZE (mtd->writesize)
 #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1))
 #define ECCBUF_MOD(x) ((x) &  (ECCBUF_SIZE - 1))
 static int
index 603a7951ac9b1e3e9b646811a840c4e8139745b8..8a0c4dec6351ff993fc5bed3f9f3df6ee1406df5 100644 (file)
@@ -569,7 +569,6 @@ void DoC2k_init(struct mtd_info *mtd)
 
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->ecctype = MTD_ECC_RS_DiskOnChip;
        mtd->size = 0;
        mtd->erasesize = 0;
        mtd->writesize = 512;
index fe71a12c26279b53dffeb5e9e13aeb91a02cc3da..6f368aec5d5d15bc21e75db9595d1c34fb9779d3 100644 (file)
@@ -348,7 +348,6 @@ void DoCMil_init(struct mtd_info *mtd)
 
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->ecctype = MTD_ECC_RS_DiskOnChip;
        mtd->size = 0;
 
        /* FIXME: erase size is not always 8KiB */
index ba4db686285af6ef6406007b44da1c2347fb0aa1..88ba82df0fbbf624d4f2885e17fafc37ab993ad7 100644 (file)
@@ -472,7 +472,6 @@ void DoCMilPlus_init(struct mtd_info *mtd)
 
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->ecctype = MTD_ECC_RS_DiskOnChip;
        mtd->size = 0;
 
        mtd->erasesize = 0;
index f457315579db924c71ba6298ec91f0e6a952af9d..bbf0553bdb2e67890a83387d30a2810f6bda5c93 100644 (file)
@@ -204,7 +204,7 @@ config MTD_ESB2ROM
 
 config MTD_CK804XROM
        tristate "BIOS flash chip on Nvidia CK804"
-       depends on X86 && MTD_JEDECPROBE
+       depends on X86 && MTD_JEDECPROBE && PCI
        help
          Support for treating the BIOS flash chip on nvidia motherboards
          as an MTD device - with this you can reprogram your BIOS.
index 78b671172bb225d6978cacd5f164c13628a39066..728aed6ad722e276e1642da0a7951b3bd61363f2 100644 (file)
@@ -205,8 +205,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
                        (((unsigned long)(window->virt)) + offset);
                map->map.size = 0xffffffffUL - map_top + 1UL;
                /* Set the name of the map to the address I am trying */
-               sprintf(map->map_name, "%s @%08lx",
-                       MOD_NAME, map->map.phys);
+               sprintf(map->map_name, "%s @%08Lx",
+                       MOD_NAME, (unsigned long long)map->map.phys);
 
                /* There is no generic VPP support */
                for(map->map.bankwidth = 32; map->map.bankwidth;
index 238d42e88ec52eeb050276480d9dcc1e7759a003..3d4a4d8ac789758b677b796cb83048ba59a93d47 100644 (file)
@@ -207,8 +207,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev,
                        (((unsigned long)(window->virt)) + offset);
                map->map.size = 0xffffffffUL - map_top + 1UL;
                /* Set the name of the map to the address I am trying */
-               sprintf(map->map_name, "%s @%08lx",
-                       MOD_NAME, map->map.phys);
+               sprintf(map->map_name, "%s @%08Lx",
+                       MOD_NAME, (unsigned long long)map->map.phys);
 
                /* There is no generic VPP support */
                for(map->map.bankwidth = 32; map->map.bankwidth;
@@ -327,7 +327,7 @@ static int __init init_ck804xrom(void)
        pdev = NULL;
 
        for(id = ck804xrom_pci_tbl; id->vendor; id++) {
-               pdev = pci_find_device(id->vendor, id->device, NULL);
+               pdev = pci_get_device(id->vendor, id->device, NULL);
                if (pdev)
                        break;
        }
index a9d808a617c9e62fe12932910dfba3e859323348..0bc013fd66a3f08570be112bca66454bff67da9b 100644 (file)
@@ -289,8 +289,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev,
                        (((unsigned long)(window->virt)) + offset);
                map->map.size = 0xffffffffUL - map_top + 1UL;
                /* Set the name of the map to the address I am trying */
-               sprintf(map->map_name, "%s @%08lx",
-                       MOD_NAME, map->map.phys);
+               sprintf(map->map_name, "%s @%08Lx",
+                       MOD_NAME, (unsigned long long)map->map.phys);
 
                /* Firmware hubs only use vpp when being programmed
                 * in a factory setting.  So in-place programming
index 2bb3e63606e5df9e343a87a81f1fe9a9e412bd04..2c884c49e84a5cd5f0bb1a50e8fc285bb408ceb3 100644 (file)
@@ -227,8 +227,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
                        (((unsigned long)(window->virt)) + offset);
                map->map.size = 0xffffffffUL - map_top + 1UL;
                /* Set the name of the map to the address I am trying */
-               sprintf(map->map_name, "%s @%08lx",
-                       MOD_NAME, map->map.phys);
+               sprintf(map->map_name, "%s @%08Lx",
+                       MOD_NAME, (unsigned long long)map->map.phys);
 
                /* Firmware hubs only use vpp when being programmed
                 * in a factory setting.  So in-place programming
index ed215470158bf808cff3e7d150715a8170c22bbd..95dcab2146ad821c90fa29df1784d62076b7d3ee 100644 (file)
@@ -94,7 +94,9 @@ static struct mtd_info *mymtd;
 
 static int __init init_netsc520(void)
 {
-       printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys);
+       printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n",
+                       (unsigned long long)netsc520_map.size,
+                       (unsigned long long)netsc520_map.phys);
        netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size);
 
        if (!netsc520_map.virt) {
index 9b50cfc355b113b411040b6d473a518ec5e92ccb..4045e372b90db10b53baaae4d15d045a881e90f9 100644 (file)
@@ -237,8 +237,9 @@ static int __init init_sc520cdp(void)
 #endif
 
        for (i = 0; i < NUM_FLASH_BANKS; i++) {
-               printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n",
-                      sc520cdp_map[i].size, sc520cdp_map[i].phys);
+               printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n",
+                       (unsigned long long)sc520cdp_map[i].size,
+                       (unsigned long long)sc520cdp_map[i].phys);
 
                sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size);
 
index 61a994ea8af1f884b078e9253039819a1a42b1d6..1592eac64e57baaefb8daecabfe700b27277247a 100644 (file)
@@ -419,8 +419,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                info.erasesize  = mtd->erasesize;
                info.writesize  = mtd->writesize;
                info.oobsize    = mtd->oobsize;
-               info.ecctype    = mtd->ecctype;
-               info.eccsize    = mtd->eccsize;
+               /* The below fields are obsolete */
+               info.ecctype    = -1;
+               info.eccsize    = 0;
                if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
                        return -EFAULT;
                break;
index 06902683bc2a7c0a71541558b76aaad032e0c578..880580c44e0134186d9a3df3fd1b81731359aa2b 100644 (file)
@@ -727,8 +727,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
        concat->mtd.erasesize = subdev[0]->erasesize;
        concat->mtd.writesize = subdev[0]->writesize;
        concat->mtd.oobsize = subdev[0]->oobsize;
-       concat->mtd.ecctype = subdev[0]->ecctype;
-       concat->mtd.eccsize = subdev[0]->eccsize;
        if (subdev[0]->writev)
                concat->mtd.writev = concat_writev;
        if (subdev[0]->read_oob)
@@ -774,8 +772,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
                if (concat->mtd.writesize   !=  subdev[i]->writesize ||
                    concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
                    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
-                   concat->mtd.ecctype    !=  subdev[i]->ecctype ||
-                   concat->mtd.eccsize    !=  subdev[i]->eccsize ||
                    !concat->mtd.read_oob  != !subdev[i]->read_oob ||
                    !concat->mtd.write_oob != !subdev[i]->write_oob) {
                        kfree(concat);
index bafd2fba87bde0f2b6f82bed155e751cbe408c12..633def3fb0875daf3a38bda7dea4cfb5d8adee77 100644 (file)
@@ -338,8 +338,6 @@ int add_mtd_partitions(struct mtd_info *master,
                slave->mtd.size = parts[i].size;
                slave->mtd.writesize = master->writesize;
                slave->mtd.oobsize = master->oobsize;
-               slave->mtd.ecctype = master->ecctype;
-               slave->mtd.eccsize = master->eccsize;
                slave->mtd.subpage_sft = master->subpage_sft;
 
                slave->mtd.name = parts[i].name;
index 358f55a82dbe4ebb73b1b0ee460063a6145e3f0e..2d12dcdd740cfd42d3b06d29b9ab947e12e98651 100644 (file)
@@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC
          incorrect ECC generation, and if using these, the default of
          software ECC is preferable.
 
-         If you lay down a device with the hardware ECC, then you will
-         currently not be able to switch to software, as there is no
-         implementation for ECC method used by the S3C2410
-
 config MTD_NAND_NDFC
        tristate "NDFC NanD Flash Controller"
        depends on MTD_NAND && 44x
@@ -221,9 +217,17 @@ config MTD_NAND_SHARPSL
        tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
        depends on MTD_NAND && ARCH_PXA
 
+config MTD_NAND_BASLER_EXCITE
+       tristate  "Support for NAND Flash on Basler eXcite"
+       depends on MTD_NAND && BASLER_EXCITE
+       help
+          This enables the driver for the NAND flash device found on the
+          Basler eXcite Smart Camera. If built as a module, the driver
+          will be named "excite_nandflash.ko".
+
 config MTD_NAND_CAFE
        tristate "NAND support for OLPC CAFÉ chip"
-       depends on PCI
+       depends on MTD_NAND && PCI
        help
         Use NAND flash attached to the CAFÉ chip designed for the $100
         laptop.
index f7a53f0b70177451680402a2c2cce4c98991cd6e..80f1dfc77949de321c45eb45d0902c5abc8bb41d 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM)                += nandsim.o
 obj-$(CONFIG_MTD_NAND_CS553X)          += cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)            += ndfc.o
 obj-$(CONFIG_MTD_NAND_AT91)            += at91_nand.o
+obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)   += excite_nandflash.o
 
 nand-objs := nand_base.o nand_bbt.o
 cafe_nand-objs := cafe.o cafe_ecc.o
index 08cb060dfa3d64aea2da088ac56f17de7c883aae..fd6bb3ed40dfb73936abf9481527915038d002a7 100644 (file)
@@ -78,8 +78,9 @@ module_param(regdebug, int, 0644);
 static int checkecc = 1;
 module_param(checkecc, int, 0644);
 
-static int slowtiming = 0;
-module_param(slowtiming, int, 0644);
+static int numtimings;
+static int timing[3];
+module_param_array(timing, int, &numtimings, 0644);
 
 /* Hrm. Why isn't this already conditional on something in the struct device? */
 #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
@@ -264,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
        ndelay(100);
 
        if (1) {
-               int c = 500000;
+               int c;
                uint32_t irqs;
 
-               while (c--) {
+               for (c = 500000; c != 0; c--) {
                        irqs = cafe_readl(cafe, NAND_IRQ);
                        if (irqs & doneint)
                                break;
@@ -529,6 +530,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
 {
        struct mtd_info *mtd;
        struct cafe_priv *cafe;
+       uint32_t timing1, timing2, timing3;
        uint32_t ctrl;
        int err = 0;
 
@@ -580,31 +582,45 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
                cafe->nand.block_bad = cafe_nand_block_bad;
        }
 
+       if (numtimings && numtimings != 3) {
+               dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings);
+       }
+
+       if (numtimings == 3) {
+               timing1 = timing[0];
+               timing2 = timing[1];
+               timing3 = timing[2];
+               cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
+                            timing1, timing2, timing3);
+       } else {
+               timing1 = cafe_readl(cafe, NAND_TIMING1);
+               timing2 = cafe_readl(cafe, NAND_TIMING2);
+               timing3 = cafe_readl(cafe, NAND_TIMING3);
+
+               if (timing1 | timing2 | timing3) {
+                       cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3);
+               } else {
+                       dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
+                       timing1 = timing2 = timing3 = 0xffffffff;
+               }
+       }
+
        /* Start off by resetting the NAND controller completely */
        cafe_writel(cafe, 1, NAND_RESET);
        cafe_writel(cafe, 0, NAND_RESET);
 
-       cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
+       cafe_writel(cafe, timing1, NAND_TIMING1);
+       cafe_writel(cafe, timing2, NAND_TIMING2);
+       cafe_writel(cafe, timing3, NAND_TIMING3);
 
-       /* Timings from Marvell's test code (not verified or calculated by us) */
-       if (!slowtiming) {
-               cafe_writel(cafe, 0x01010a0a, NAND_TIMING1);
-               cafe_writel(cafe, 0x24121212, NAND_TIMING2);
-               cafe_writel(cafe, 0x11000000, NAND_TIMING3);
-       } else {
-               cafe_writel(cafe, 0xffffffff, NAND_TIMING1);
-               cafe_writel(cafe, 0xffffffff, NAND_TIMING2);
-               cafe_writel(cafe, 0xffffffff, NAND_TIMING3);
-       }
        cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
        err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
                          "CAFE NAND", mtd);
        if (err) {
                dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
-
                goto out_free_dma;
        }
-#if 1
+
        /* Disable master reset, enable NAND clock */
        ctrl = cafe_readl(cafe, GLOBAL_CTRL);
        ctrl &= 0xffffeff0;
@@ -631,32 +647,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
        cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
        cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
                cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
-#endif
-#if 1
-       mtd->writesize=2048;
-       mtd->oobsize = 0x40;
-       memset(cafe->dmabuf, 0x5a, 2112);
-       cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1);
-       cafe->nand.read_byte(mtd);
-       cafe->nand.read_byte(mtd);
-       cafe->nand.read_byte(mtd);
-       cafe->nand.read_byte(mtd);
-       cafe->nand.read_byte(mtd);
-#endif
-#if 0
-       cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0);
-       //      nand_wait_ready(mtd);
-       cafe->nand.read_byte(mtd);
-       cafe->nand.read_byte(mtd);
-       cafe->nand.read_byte(mtd);
-       cafe->nand.read_byte(mtd);
-#endif
-#if 0
-       writel(0x84600070, cafe->mmio);
-       udelay(10);
-       cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM));
-#endif
-       /* Scan to find existance of the device */
+
+       /* Scan to find existence of the device */
        if (nand_scan_ident(mtd, 1)) {
                err = -ENXIO;
                goto out_irq;
@@ -760,13 +752,4 @@ module_exit(cafe_nand_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip");
-
-/* Correct ECC for 2048 bytes of 0xff:
-   41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */
-
-/* dwmw2's B-test board, in case of completely screwing it:
-Bad eraseblock 2394 at 0x12b40000
-Bad eraseblock 2627 at 0x14860000
-Bad eraseblock 3349 at 0x1a2a0000
-*/
+MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip");
index 1b9fa05a44747fdb7eeb9bb9049dc47e6131edf1..ea5c8491d2c57677250b012f1d9261494cead565 100644 (file)
@@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = {
 
 static unsigned short err_pos(unsigned short din)
 {
-       BUG_ON(din > 4096);
+       BUG_ON(din >= ARRAY_SIZE(err_pos_lut));
        return err_pos_lut[din];
 }
 static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c
new file mode 100644 (file)
index 0000000..7e9afc4
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+*  Copyright (C) 2005 - 2007 by Basler Vision Technologies AG
+*  Author: Thomas Koeller <thomas.koeller.qbaslerweb.com>
+*  Original code by Thies Moeller <thies.moeller@baslerweb.com>
+*
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  This program is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  You should have received a copy of the GNU General Public License
+*  along with this program; if not, write to the Free Software
+*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/rm9k-ocd.h>
+
+#include <excite_nandflash.h>
+
+#define EXCITE_NANDFLASH_VERSION "0.1"
+
+/* I/O register offsets */
+#define EXCITE_NANDFLASH_DATA_BYTE   0x00
+#define EXCITE_NANDFLASH_STATUS_BYTE 0x0c
+#define EXCITE_NANDFLASH_ADDR_BYTE   0x10
+#define EXCITE_NANDFLASH_CMD_BYTE    0x14
+
+/* prefix for debug output */
+static const char module_id[] = "excite_nandflash";
+
+/*
+ * partition definition
+ */
+static const struct mtd_partition partition_info[] = {
+       {
+               .name = "eXcite RootFS",
+               .offset = 0,
+               .size = MTDPART_SIZ_FULL
+       }
+};
+
+static inline const struct resource *
+excite_nand_get_resource(struct platform_device *d, unsigned long flags,
+                        const char *basename)
+{
+       char buf[80];
+
+       if (snprintf(buf, sizeof buf, "%s_%u", basename, d->id) >= sizeof buf)
+               return NULL;
+       return platform_get_resource_byname(d, flags, buf);
+}
+
+static inline void __iomem *
+excite_nand_map_regs(struct platform_device *d, const char *basename)
+{
+       void *result = NULL;
+       const struct resource *const r =
+           excite_nand_get_resource(d, IORESOURCE_MEM, basename);
+
+       if (r)
+               result = ioremap_nocache(r->start, r->end + 1 - r->start);
+       return result;
+}
+
+/* controller and mtd information */
+struct excite_nand_drvdata {
+       struct mtd_info board_mtd;
+       struct nand_chip board_chip;
+       void __iomem *regs;
+       void __iomem *tgt;
+};
+
+/* Control function */
+static void excite_nand_control(struct mtd_info *mtd, int cmd,
+                                      unsigned int ctrl)
+{
+       struct excite_nand_drvdata * const d =
+           container_of(mtd, struct excite_nand_drvdata, board_mtd);
+
+       switch (ctrl) {
+       case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
+               d->tgt = d->regs + EXCITE_NANDFLASH_CMD_BYTE;
+               break;
+       case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
+               d->tgt = d->regs + EXCITE_NANDFLASH_ADDR_BYTE;
+               break;
+       case NAND_CTRL_CHANGE | NAND_NCE:
+               d->tgt = d->regs + EXCITE_NANDFLASH_DATA_BYTE;
+               break;
+       }
+
+       if (cmd != NAND_CMD_NONE)
+               __raw_writeb(cmd, d->tgt);
+}
+
+/* Return 0 if flash is busy, 1 if ready */
+static int excite_nand_devready(struct mtd_info *mtd)
+{
+       struct excite_nand_drvdata * const drvdata =
+           container_of(mtd, struct excite_nand_drvdata, board_mtd);
+
+       return __raw_readb(drvdata->regs + EXCITE_NANDFLASH_STATUS_BYTE);
+}
+
+/*
+ * Called by device layer to remove the driver.
+ * The binding to the mtd and all allocated
+ * resources are released.
+ */
+static int __exit excite_nand_remove(struct device *dev)
+{
+       struct excite_nand_drvdata * const this = dev_get_drvdata(dev);
+
+       dev_set_drvdata(dev, NULL);
+
+       if (unlikely(!this)) {
+               printk(KERN_ERR "%s: called %s without private data!!",
+                      module_id, __func__);
+               return -EINVAL;
+       }
+
+       /* first thing we need to do is release our mtd
+        * then go through freeing the resource used
+        */
+       nand_release(&this->board_mtd);
+
+       /* free the common resources */
+       iounmap(this->regs);
+       kfree(this);
+
+       DEBUG(MTD_DEBUG_LEVEL1, "%s: removed\n", module_id);
+       return 0;
+}
+
+/*
+ * Called by device layer when it finds a device matching
+ * one our driver can handle. This code checks to see if
+ * it can allocate all necessary resources then calls the
+ * nand layer to look for devices.
+*/
+static int __init excite_nand_probe(struct device *dev)
+{
+       struct platform_device * const pdev = to_platform_device(dev);
+       struct excite_nand_drvdata *drvdata;    /* private driver data */
+       struct nand_chip *board_chip;   /* private flash chip data */
+       struct mtd_info *board_mtd;     /* mtd info for this board */
+       int scan_res;
+
+       drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
+       if (unlikely(!drvdata)) {
+               printk(KERN_ERR "%s: no memory for drvdata\n",
+                      module_id);
+               return -ENOMEM;
+       }
+
+       /* bind private data into driver */
+       dev_set_drvdata(dev, drvdata);
+
+       /* allocate and map the resource */
+       drvdata->regs =
+               excite_nand_map_regs(pdev, EXCITE_NANDFLASH_RESOURCE_REGS);
+
+       if (unlikely(!drvdata->regs)) {
+               printk(KERN_ERR "%s: cannot reserve register region\n",
+                      module_id);
+               kfree(drvdata);
+               return -ENXIO;
+       }
+       
+       drvdata->tgt = drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE;
+
+       /* initialise our chip */
+       board_chip = &drvdata->board_chip;
+       board_chip->IO_ADDR_R = board_chip->IO_ADDR_W =
+               drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE;
+       board_chip->cmd_ctrl = excite_nand_control;
+       board_chip->dev_ready = excite_nand_devready;
+       board_chip->chip_delay = 25;
+       board_chip->ecc.mode = NAND_ECC_SOFT;
+
+       /* link chip to mtd */
+       board_mtd = &drvdata->board_mtd;
+       board_mtd->priv = board_chip;
+
+       DEBUG(MTD_DEBUG_LEVEL2, "%s: device scan\n", module_id);
+       scan_res = nand_scan(&drvdata->board_mtd, 1);
+
+       if (likely(!scan_res)) {
+               DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id);
+               add_mtd_partitions(&drvdata->board_mtd, partition_info,
+                                  sizeof partition_info / sizeof partition_info[0]);
+       } else {
+               iounmap(drvdata->regs);
+               kfree(drvdata);
+               printk(KERN_ERR "%s: device scan failed\n", module_id);
+               return -EIO;
+       }
+       return 0;
+}
+
+static struct device_driver excite_nand_driver = {
+       .name = "excite_nand",
+       .bus = &platform_bus_type,
+       .probe = excite_nand_probe,
+       .remove = __exit_p(excite_nand_remove)
+};
+
+static int __init excite_nand_init(void)
+{
+       pr_info("Basler eXcite nand flash driver Version "
+               EXCITE_NANDFLASH_VERSION "\n");
+       return driver_register(&excite_nand_driver);
+}
+
+static void __exit excite_nand_exit(void)
+{
+       driver_unregister(&excite_nand_driver);
+}
+
+module_init(excite_nand_init);
+module_exit(excite_nand_exit);
+
+MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>");
+MODULE_DESCRIPTION("Basler eXcite NAND-Flash driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EXCITE_NANDFLASH_VERSION)
index dfe56e03e48b0bf9ca9bfa89fc4b11e5e15d4dc6..acaf97bc80d18f89696987db2c0370fd8cac0b76 100644 (file)
@@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
        DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
              (unsigned long long)from, readlen);
 
-       if (ops->mode == MTD_OOB_RAW)
-               len = mtd->oobsize;
-       else
+       if (ops->mode == MTD_OOB_AUTO)
                len = chip->ecc.layout->oobavail;
+       else
+               len = mtd->oobsize;
+
+       if (unlikely(ops->ooboffs >= len)) {
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+                       "Attempt to start read outside oob\n");
+               return -EINVAL;
+       }
+
+       /* Do not allow reads past end of device */
+       if (unlikely(from >= mtd->size ||
+                    ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
+                                       (from >> chip->page_shift)) * len)) {
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+                       "Attempt read beyond end of device\n");
+               return -EINVAL;
+       }
 
        chipnr = (int)(from >> chip->chip_shift);
        chip->select_chip(mtd, chipnr);
@@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
 static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
                             struct mtd_oob_ops *ops)
 {
-       int chipnr, page, status;
+       int chipnr, page, status, len;
        struct nand_chip *chip = mtd->priv;
 
        DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
              (unsigned int)to, (int)ops->ooblen);
 
+       if (ops->mode == MTD_OOB_AUTO)
+               len = chip->ecc.layout->oobavail;
+       else
+               len = mtd->oobsize;
+
        /* Do not allow write past end of page */
-       if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) {
+       if ((ops->ooboffs + ops->ooblen) > len) {
                DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
                      "Attempt to write past end of page\n");
                return -EINVAL;
        }
 
+       if (unlikely(ops->ooboffs >= len)) {
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+                       "Attempt to start write outside oob\n");
+               return -EINVAL;
+       }
+
+       /* Do not allow reads past end of device */
+       if (unlikely(to >= mtd->size ||
+                    ops->ooboffs + ops->ooblen >
+                       ((mtd->size >> chip->page_shift) -
+                        (to >> chip->page_shift)) * len)) {
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+                       "Attempt write beyond end of device\n");
+               return -EINVAL;
+       }
+
        chipnr = (int)(to >> chip->chip_shift);
        chip->select_chip(mtd, chipnr);
 
@@ -2530,7 +2566,6 @@ int nand_scan_tail(struct mtd_info *mtd)
        /* Fill in remaining MTD driver data */
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->ecctype = MTD_ECC_SW;
        mtd->erase = nand_erase;
        mtd->point = NULL;
        mtd->unpoint = NULL;
index 8b3203571eeb3e0d7afbb36bc9c8b092ff49b840..0ddfd6de75c52e75864c6cb6ab7d7550e45089dd 100644 (file)
@@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd)
 static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
                                     u_char *read_ecc, u_char *calc_ecc)
 {
-       pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       unsigned int diff0, diff1, diff2;
+       unsigned int bit, byte;
 
-       pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
-                read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
+       pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc);
 
-       if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2])
-               return 0;
+       diff0 = read_ecc[0] ^ calc_ecc[0];
+       diff1 = read_ecc[1] ^ calc_ecc[1];
+       diff2 = read_ecc[2] ^ calc_ecc[2];
+
+       pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n",
+                __func__,
+                read_ecc[0], read_ecc[1], read_ecc[2],
+                calc_ecc[0], calc_ecc[1], calc_ecc[2],
+                diff0, diff1, diff2);
+
+       if (diff0 == 0 && diff1 == 0 && diff2 == 0)
+               return 0;               /* ECC is ok */
+
+       /* Can we correct this ECC (ie, one row and column change).
+        * Note, this is similar to the 256 error code on smartmedia */
+
+       if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&
+           ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&
+           ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
+               /* calculate the bit position of the error */
+
+               bit  = (diff2 >> 2) & 1;
+               bit |= (diff2 >> 3) & 2;
+               bit |= (diff2 >> 4) & 4;
+
+               /* calculate the byte position of the error */
+
+               byte  = (diff1 << 1) & 0x80;
+               byte |= (diff1 << 2) & 0x40;
+               byte |= (diff1 << 3) & 0x20;
+               byte |= (diff1 << 4) & 0x10;
+
+               byte |= (diff0 >> 3) & 0x08;
+               byte |= (diff0 >> 2) & 0x04;
+               byte |= (diff0 >> 1) & 0x02;
+               byte |= (diff0 >> 0) & 0x01;
 
-       /* we curently have no method for correcting the error */
+               byte |= (diff2 << 8) & 0x100;
 
-       return -1;
+               dev_dbg(info->device, "correcting error bit %d, byte %d\n",
+                       bit, byte);
+
+               dat[byte] ^= (1 << bit);
+               return 1;
+       }
+
+       /* if there is only one bit difference in the ECC, then
+        * one of only a row or column parity has changed, which
+        * means the error is most probably in the ECC itself */
+
+       diff0 |= (diff1 << 8);
+       diff0 |= (diff2 << 16);
+
+       if ((diff0 & ~(1<<fls(diff0))) == 0)
+               return 1;
+
+       return 0;
 }
 
 /* ECC functions
@@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
        writel(ctrl, info->regs + S3C2410_NFCONF);
 }
 
+static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       unsigned long ctrl;
+
+       ctrl = readl(info->regs + S3C2440_NFCONT);
+       writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT);
+}
+
 static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
 {
        struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
@@ -383,6 +444,21 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
        ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
        ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
 
+       pr_debug("%s: returning ecc %02x%02x%02x\n", __func__,
+                ecc_code[0], ecc_code[1], ecc_code[2]);
+
+       return 0;
+}
+
+static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       unsigned long ecc = readl(info->regs + S3C2412_NFMECC0);
+
+       ecc_code[0] = ecc;
+       ecc_code[1] = ecc >> 8;
+       ecc_code[2] = ecc >> 16;
+
        pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
 
        return 0;
@@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
        ecc_code[1] = ecc >> 8;
        ecc_code[2] = ecc >> 16;
 
-       pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
+       pr_debug("%s: returning ecc %06x\n", __func__, ecc);
 
        return 0;
 }
@@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
                        break;
 
                case TYPE_S3C2412:
+                       chip->ecc.hwctl     = s3c2412_nand_enable_hwecc;
+                       chip->ecc.calculate = s3c2412_nand_calculate_ecc;
+                       break;
+
                case TYPE_S3C2440:
                        chip->ecc.hwctl     = s3c2440_nand_enable_hwecc;
                        chip->ecc.calculate = s3c2440_nand_calculate_ecc;
index 2da6bb26353e22c6966c0835cf596cb227ac1883..7f1cb6e5dccb5b8463554cbf48e5227b66b1597d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  linux/drivers/mtd/onenand/onenand_base.c
  *
- *  Copyright (C) 2005-2006 Samsung Electronics
+ *  Copyright (C) 2005-2007 Samsung Electronics
  *  Kyungmin Park <kyungmin.park@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -94,16 +94,9 @@ static void onenand_writew(unsigned short value, void __iomem *addr)
  */
 static int onenand_block_address(struct onenand_chip *this, int block)
 {
-       if (this->device_id & ONENAND_DEVICE_IS_DDP) {
-               /* Device Flash Core select, NAND Flash Block Address */
-               int dfs = 0;
-
-               if (block & this->density_mask)
-                       dfs = 1;
-
-               return (dfs << ONENAND_DDP_SHIFT) |
-                       (block & (this->density_mask - 1));
-       }
+       /* Device Flash Core select, NAND Flash Block Address */
+       if (block & this->density_mask)
+               return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);
 
        return block;
 }
@@ -118,17 +111,11 @@ static int onenand_block_address(struct onenand_chip *this, int block)
  */
 static int onenand_bufferram_address(struct onenand_chip *this, int block)
 {
-       if (this->device_id & ONENAND_DEVICE_IS_DDP) {
-               /* Device BufferRAM Select */
-               int dbs = 0;
-
-               if (block & this->density_mask)
-                       dbs = 1;
+       /* Device BufferRAM Select */
+       if (block & this->density_mask)
+               return ONENAND_DDP_CHIP1;
 
-               return (dbs << ONENAND_DDP_SHIFT);
-       }
-
-       return 0;
+       return ONENAND_DDP_CHIP0;
 }
 
 /**
@@ -317,22 +304,25 @@ static int onenand_wait(struct mtd_info *mtd, int state)
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
        if (ctrl & ONENAND_CTRL_ERROR) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl);
+               printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl);
                if (ctrl & ONENAND_CTRL_LOCK)
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n");
+                       printk(KERN_ERR "onenand_wait: it's locked error.\n");
                return ctrl;
        }
 
        if (interrupt & ONENAND_INT_READ) {
                int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
                if (ecc) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
+                       printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
                        if (ecc & ONENAND_ECC_2BIT_ALL) {
                                mtd->ecc_stats.failed++;
                                return ecc;
                        } else if (ecc & ONENAND_ECC_1BIT_ALL)
                                mtd->ecc_stats.corrected++;
                }
+       } else if (state == FL_READING) {
+               printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
+               return -EIO;
        }
 
        return 0;
@@ -587,22 +577,32 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
 static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
 {
        struct onenand_chip *this = mtd->priv;
-       int block, page;
-       int i;
+       int blockpage, found = 0;
+       unsigned int i;
 
-       block = (int) (addr >> this->erase_shift);
-       page = (int) (addr >> this->page_shift);
-       page &= this->page_mask;
+       blockpage = (int) (addr >> this->page_shift);
 
+       /* Is there valid data? */
        i = ONENAND_CURRENT_BUFFERRAM(this);
+       if (this->bufferram[i].blockpage == blockpage)
+               found = 1;
+       else {
+               /* Check another BufferRAM */
+               i = ONENAND_NEXT_BUFFERRAM(this);
+               if (this->bufferram[i].blockpage == blockpage) {
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
+                       found = 1;
+               }
+       }
 
-       /* Is there valid data? */
-       if (this->bufferram[i].block == block &&
-           this->bufferram[i].page == page &&
-           this->bufferram[i].valid)
-               return 1;
+       if (found && ONENAND_IS_DDP(this)) {
+               /* Select DataRAM for DDP */
+               int block = (int) (addr >> this->erase_shift);
+               int value = onenand_bufferram_address(this, block);
+               this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+       }
 
-       return 0;
+       return found;
 }
 
 /**
@@ -613,31 +613,49 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
  *
  * Update BufferRAM information
  */
-static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
+static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
                int valid)
 {
        struct onenand_chip *this = mtd->priv;
-       int blockpage;
-       int i;
+       int blockpage;
+       unsigned int i;
 
-       block = (int) (addr >> this->erase_shift);
-       page = (int) (addr >> this->page_shift);
-       page &= this->page_mask;
+       blockpage = (int) (addr >> this->page_shift);
 
-       /* Invalidate BufferRAM */
-       for (i = 0; i < MAX_BUFFERRAM; i++) {
-               if (this->bufferram[i].block == block &&
-                   this->bufferram[i].page == page)
-                       this->bufferram[i].valid = 0;
-       }
+       /* Invalidate another BufferRAM */
+       i = ONENAND_NEXT_BUFFERRAM(this);
+       if (this->bufferram[i].blockpage == blockpage)
+               this->bufferram[i].blockpage = -1;
 
        /* Update BufferRAM */
        i = ONENAND_CURRENT_BUFFERRAM(this);
-       this->bufferram[i].block = block;
-       this->bufferram[i].page = page;
-       this->bufferram[i].valid = valid;
+       if (valid)
+               this->bufferram[i].blockpage = blockpage;
+       else
+               this->bufferram[i].blockpage = -1;
+}
 
-       return 0;
+/**
+ * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
+ * @param mtd          MTD data structure
+ * @param addr         start address to invalidate
+ * @param len          length to invalidate
+ *
+ * Invalidate BufferRAM information
+ */
+static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
+               unsigned int len)
+{
+       struct onenand_chip *this = mtd->priv;
+       int i;
+       loff_t end_addr = addr + len;
+
+       /* Invalidate BufferRAM */
+       for (i = 0; i < MAX_BUFFERRAM; i++) {
+               loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
+               if (buf_addr >= addr && buf_addr < end_addr)
+                       this->bufferram[i].blockpage = -1;
+       }
 }
 
 /**
@@ -716,7 +734,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
 
        /* Do not allow reads past end of device */
        if ((from + len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n");
+               printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n");
                *retlen = 0;
                return -EINVAL;
        }
@@ -724,8 +742,6 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
        /* Grab the lock and see if the device is available */
        onenand_get_device(mtd, FL_READING);
 
-       /* TODO handling oob */
-
        stats = mtd->ecc_stats;
 
        /* Read-while-load method */
@@ -754,9 +770,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
                         * Now we issued chip 1 read and pointed chip 1
                         * bufferam so we have to point chip 0 bufferam.
                         */
-                       if (this->device_id & ONENAND_DEVICE_IS_DDP &&
-                                       unlikely(from == (this->chipsize >> 1))) {
-                               this->write_word(0, this->base + ONENAND_REG_START_ADDRESS2);
+                       if (ONENAND_IS_DDP(this) &&
+                           unlikely(from == (this->chipsize >> 1))) {
+                               this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
                                boundary = 1;
                        } else
                                boundary = 0;
@@ -770,7 +786,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
                        break;
                /* Set up for next read from bufferRAM */
                if (unlikely(boundary))
-                       this->write_word(0x8000, this->base + ONENAND_REG_START_ADDRESS2);
+                       this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
                ONENAND_SET_NEXT_BUFFERRAM(this);
                buf += thislen;
                thislen = min_t(int, mtd->writesize, len - read);
@@ -800,6 +816,44 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
        return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
 }
 
+/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd          MTD device structure
+ * @param buf          destination address
+ * @param column       oob offset to read from
+ * @param thislen      oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+                               int thislen)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct nand_oobfree *free;
+       int readcol = column;
+       int readend = column + thislen;
+       int lastgap = 0;
+       uint8_t *oob_buf = this->page_buf + mtd->writesize;
+
+       for (free = this->ecclayout->oobfree; free->length; ++free) {
+               if (readcol >= lastgap)
+                       readcol += free->offset - lastgap;
+               if (readend >= lastgap)
+                       readend += free->offset - lastgap;
+               lastgap = free->offset + free->length;
+       }
+       this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
+       for (free = this->ecclayout->oobfree; free->length; ++free) {
+               int free_end = free->offset + free->length;
+               if (free->offset < readend && free_end > readcol) {
+                       int st = max_t(int,free->offset,readcol);
+                       int ed = min_t(int,free_end,readend);
+                       int n = ed - st;
+                       memcpy(buf, oob_buf + st, n);
+                       buf += n;
+               }
+       }
+       return 0;
+}
+
 /**
  * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
  * @param mtd          MTD device structure
@@ -807,14 +861,15 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
  * @param len          number of bytes to read
  * @param retlen       pointer to variable to store the number of read bytes
  * @param buf          the databuffer to put data
+ * @param mode         operation mode
  *
  * OneNAND read out-of-band data from the spare area
  */
-int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-                       size_t *retlen, u_char *buf)
+static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+                       size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
 {
        struct onenand_chip *this = mtd->priv;
-       int read = 0, thislen, column;
+       int read = 0, thislen, column, oobsize;
        int ret = 0;
 
        DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
@@ -822,21 +877,33 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
        /* Initialize return length value */
        *retlen = 0;
 
+       if (mode == MTD_OOB_AUTO)
+               oobsize = this->ecclayout->oobavail;
+       else
+               oobsize = mtd->oobsize;
+
+       column = from & (mtd->oobsize - 1);
+
+       if (unlikely(column >= oobsize)) {
+               printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n");
+               return -EINVAL;
+       }
+
        /* Do not allow reads past end of device */
-       if (unlikely((from + len) > mtd->size)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n");
+       if (unlikely(from >= mtd->size ||
+                    column + len > ((mtd->size >> this->page_shift) -
+                                    (from >> this->page_shift)) * oobsize)) {
+               printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n");
                return -EINVAL;
        }
 
        /* Grab the lock and see if the device is available */
        onenand_get_device(mtd, FL_READING);
 
-       column = from & (mtd->oobsize - 1);
-
        while (read < len) {
                cond_resched();
 
-               thislen = mtd->oobsize - column;
+               thislen = oobsize - column;
                thislen = min_t(int, thislen, len);
 
                this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
@@ -846,11 +913,14 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
                ret = this->wait(mtd, FL_READING);
                /* First copy data and check return value for ECC handling */
 
-               this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+               if (mode == MTD_OOB_AUTO)
+                       onenand_transfer_auto_oob(mtd, buf, column, thislen);
+               else
+                       this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
 
                if (ret) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
-                       goto out;
+                       printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret);
+                       break;
                }
 
                read += thislen;
@@ -868,7 +938,6 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
                }
        }
 
-out:
        /* Deselect and wake up anyone waiting on the device */
        onenand_release_device(mtd);
 
@@ -885,10 +954,132 @@ out:
 static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
                            struct mtd_oob_ops *ops)
 {
-       BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+       switch (ops->mode) {
+       case MTD_OOB_PLACE:
+       case MTD_OOB_AUTO:
+               break;
+       case MTD_OOB_RAW:
+               /* Not implemented yet */
+       default:
+               return -EINVAL;
+       }
        return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
-                                  &ops->oobretlen, ops->oobbuf);
+                                  &ops->oobretlen, ops->oobbuf, ops->mode);
+}
+
+/**
+ * onenand_bbt_wait - [DEFAULT] wait until the command is done
+ * @param mtd          MTD device structure
+ * @param state                state to select the max. timeout value
+ *
+ * Wait for command done.
+ */
+static int onenand_bbt_wait(struct mtd_info *mtd, int state)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned long timeout;
+       unsigned int interrupt;
+       unsigned int ctrl;
+
+       /* The 20 msec is enough */
+       timeout = jiffies + msecs_to_jiffies(20);
+       while (time_before(jiffies, timeout)) {
+               interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+               if (interrupt & ONENAND_INT_MASTER)
+                       break;
+       }
+       /* To get correct interrupt status in timeout case */
+       interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+       ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+
+       if (ctrl & ONENAND_CTRL_ERROR) {
+               printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
+               /* Initial bad block case */
+               if (ctrl & ONENAND_CTRL_LOAD)
+                       return ONENAND_BBT_READ_ERROR;
+               return ONENAND_BBT_READ_FATAL_ERROR;
+       }
+
+       if (interrupt & ONENAND_INT_READ) {
+               int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+               if (ecc & ONENAND_ECC_2BIT_ALL)
+                       return ONENAND_BBT_READ_ERROR;
+       } else {
+               printk(KERN_ERR "onenand_bbt_wait: read timeout!"
+                       "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
+               return ONENAND_BBT_READ_FATAL_ERROR;
+       }
+
+       return 0;
+}
+
+/**
+ * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
+ * @param mtd          MTD device structure
+ * @param from         offset to read from
+ * @param @ops         oob operation description structure
+ *
+ * OneNAND read out-of-band data from the spare area for bbt scan
+ */
+int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, 
+                           struct mtd_oob_ops *ops)
+{
+       struct onenand_chip *this = mtd->priv;
+       int read = 0, thislen, column;
+       int ret = 0;
+       size_t len = ops->ooblen;
+       u_char *buf = ops->oobbuf;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
+
+       /* Initialize return value */
+       ops->oobretlen = 0;
+
+       /* Do not allow reads past end of device */
+       if (unlikely((from + len) > mtd->size)) {
+               printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");
+               return ONENAND_BBT_READ_FATAL_ERROR;
+       }
+
+       /* Grab the lock and see if the device is available */
+       onenand_get_device(mtd, FL_READING);
+
+       column = from & (mtd->oobsize - 1);
+
+       while (read < len) {
+               cond_resched();
+
+               thislen = mtd->oobsize - column;
+               thislen = min_t(int, thislen, len);
+
+               this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+
+               onenand_update_bufferram(mtd, from, 0);
+
+               ret = onenand_bbt_wait(mtd, FL_READING);
+               if (ret)
+                       break;
+
+               this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+               read += thislen;
+               if (read == len)
+                       break;
+
+               buf += thislen;
+
+               /* Read more? */
+               if (read < len) {
+                       /* Update Page size */
+                       from += mtd->writesize;
+                       column = 0;
+               }
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       onenand_release_device(mtd);
+
+       ops->oobretlen = read;
+       return ret;
 }
 
 #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -897,14 +1088,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
  * @param mtd          MTD device structure
  * @param buf          the databuffer to verify
  * @param to           offset to read from
- * @param len          number of bytes to read and compare
  *
  */
-static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len)
+static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
 {
        struct onenand_chip *this = mtd->priv;
-       char *readp = this->page_buf;
-       int column = to & (mtd->oobsize - 1);
+       char *readp = this->page_buf + mtd->writesize;
        int status, i;
 
        this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@@ -913,9 +1102,8 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
        if (status)
                return status;
 
-       this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len);
-
-       for(i = 0; i < len; i++)
+       this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize);
+       for(i = 0; i < mtd->oobsize; i++)
                if (buf[i] != 0xFF && buf[i] != readp[i])
                        return -EBADMSG;
 
@@ -923,41 +1111,51 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
 }
 
 /**
- * onenand_verify_page - [GENERIC] verify the chip contents after a write
- * @param mtd          MTD device structure
- * @param buf          the databuffer to verify
+ * onenand_verify - [GENERIC] verify the chip contents after a write
+ * @param mtd          MTD device structure
+ * @param buf          the databuffer to verify
+ * @param addr         offset to read from
+ * @param len          number of bytes to read and compare
  *
- * Check DataRAM area directly
  */
-static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr)
+static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
 {
        struct onenand_chip *this = mtd->priv;
-       void __iomem *dataram0, *dataram1;
+       void __iomem *dataram;
        int ret = 0;
+       int thislen, column;
 
-       /* In partial page write, just skip it */
-       if ((addr & (mtd->writesize - 1)) != 0)
-               return 0;
+       while (len != 0) {
+               thislen = min_t(int, mtd->writesize, len);
+               column = addr & (mtd->writesize - 1);
+               if (column + thislen > mtd->writesize)
+                       thislen = mtd->writesize - column;
 
-       this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
+               this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
 
-       ret = this->wait(mtd, FL_READING);
-       if (ret)
-               return ret;
+               onenand_update_bufferram(mtd, addr, 0);
+
+               ret = this->wait(mtd, FL_READING);
+               if (ret)
+                       return ret;
 
-       onenand_update_bufferram(mtd, addr, 1);
+               onenand_update_bufferram(mtd, addr, 1);
 
-       /* Check, if the two dataram areas are same */
-       dataram0 = this->base + ONENAND_DATARAM;
-       dataram1 = dataram0 + mtd->writesize;
+               dataram = this->base + ONENAND_DATARAM;
+               dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
 
-       if (memcmp(dataram0, dataram1, mtd->writesize))
-               return -EBADMSG;
+               if (memcmp(buf, dataram + column, thislen))
+                       return -EBADMSG;
+
+               len -= thislen;
+               buf += thislen;
+               addr += thislen;
+       }
 
        return 0;
 }
 #else
-#define onenand_verify_page(...)       (0)
+#define onenand_verify(...)            (0)
 #define onenand_verify_oob(...)                (0)
 #endif
 
@@ -988,60 +1186,57 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
 
        /* Do not allow writes past end of device */
        if (unlikely((to + len) > mtd->size)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n");
+               printk(KERN_ERR "onenand_write: Attempt write to past end of device\n");
                return -EINVAL;
        }
 
        /* Reject writes, which are not page aligned */
         if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
-                DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n");
+                printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n");
                 return -EINVAL;
         }
 
        column = to & (mtd->writesize - 1);
-       subpage = column || (len & (mtd->writesize - 1));
 
        /* Grab the lock and see if the device is available */
        onenand_get_device(mtd, FL_WRITING);
 
        /* Loop until all data write */
        while (written < len) {
-               int bytes = mtd->writesize;
-               int thislen = min_t(int, bytes, len - written);
+               int thislen = min_t(int, mtd->writesize - column, len - written);
                u_char *wbuf = (u_char *) buf;
 
                cond_resched();
 
-               this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes);
+               this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
 
                /* Partial page write */
+               subpage = thislen < mtd->writesize;
                if (subpage) {
-                       bytes = min_t(int, bytes - column, (int) len);
                        memset(this->page_buf, 0xff, mtd->writesize);
-                       memcpy(this->page_buf + column, buf, bytes);
+                       memcpy(this->page_buf + column, buf, thislen);
                        wbuf = this->page_buf;
-                       /* Even though partial write, we need page size */
-                       thislen = mtd->writesize;
                }
 
-               this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen);
+               this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
                this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
 
                this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
 
+               ret = this->wait(mtd, FL_WRITING);
+
                /* In partial page write we don't update bufferram */
-               onenand_update_bufferram(mtd, to, !subpage);
+               onenand_update_bufferram(mtd, to, !ret && !subpage);
 
-               ret = this->wait(mtd, FL_WRITING);
                if (ret) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret);
+                       printk(KERN_ERR "onenand_write: write filaed %d\n", ret);
                        break;
                }
 
                /* Only check verify write turn on */
-               ret = onenand_verify_page(mtd, (u_char *) wbuf, to);
+               ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
                if (ret) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret);
+                       printk(KERN_ERR "onenand_write: verify failed %d\n", ret);
                        break;
                }
 
@@ -1063,6 +1258,43 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
        return ret;
 }
 
+/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd          MTD device structure
+ * @param oob_buf      oob buffer
+ * @param buf          source address
+ * @param column       oob offset to write to
+ * @param thislen      oob length to write
+ */
+static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+                                 const u_char *buf, int column, int thislen)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct nand_oobfree *free;
+       int writecol = column;
+       int writeend = column + thislen;
+       int lastgap = 0;
+
+       for (free = this->ecclayout->oobfree; free->length; ++free) {
+               if (writecol >= lastgap)
+                       writecol += free->offset - lastgap;
+               if (writeend >= lastgap)
+                       writeend += free->offset - lastgap;
+               lastgap = free->offset + free->length;
+       }
+       for (free = this->ecclayout->oobfree; free->length; ++free) {
+               int free_end = free->offset + free->length;
+               if (free->offset < writeend && free_end > writecol) {
+                       int st = max_t(int,free->offset,writecol);
+                       int ed = min_t(int,free_end,writeend);
+                       int n = ed - st;
+                       memcpy(oob_buf + st, buf, n);
+                       buf += n;
+               }
+       }
+       return 0;
+}
+
 /**
  * onenand_do_write_oob - [Internal] OneNAND write out-of-band
  * @param mtd          MTD device structure
@@ -1070,14 +1302,15 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
  * @param len          number of bytes to write
  * @param retlen       pointer to variable to store the number of written bytes
  * @param buf          the data to write
+ * @param mode         operation mode
  *
  * OneNAND write out-of-band
  */
 static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
-                               size_t *retlen, const u_char *buf)
+                               size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
 {
        struct onenand_chip *this = mtd->priv;
-       int column, ret = 0;
+       int column, ret = 0, oobsize;
        int written = 0;
 
        DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -1085,9 +1318,30 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
        /* Initialize retlen, in case of early exit */
        *retlen = 0;
 
-       /* Do not allow writes past end of device */
-       if (unlikely((to + len) > mtd->size)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n");
+       if (mode == MTD_OOB_AUTO)
+               oobsize = this->ecclayout->oobavail;
+       else
+               oobsize = mtd->oobsize;
+
+       column = to & (mtd->oobsize - 1);
+
+       if (unlikely(column >= oobsize)) {
+               printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n");
+               return -EINVAL;
+       }
+
+       /* For compatibility with NAND: Do not allow write past end of page */
+       if (column + len > oobsize) {
+               printk(KERN_ERR "onenand_write_oob: "
+                     "Attempt to write past end of page\n");
+               return -EINVAL;
+       }
+
+       /* Do not allow reads past end of device */
+       if (unlikely(to >= mtd->size ||
+                    column + len > ((mtd->size >> this->page_shift) -
+                                    (to >> this->page_shift)) * oobsize)) {
+               printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n");
                return -EINVAL;
        }
 
@@ -1096,18 +1350,19 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
 
        /* Loop until all data write */
        while (written < len) {
-               int thislen = min_t(int, mtd->oobsize, len - written);
+               int thislen = min_t(int, oobsize, len - written);
 
                cond_resched();
 
-               column = to & (mtd->oobsize - 1);
-
                this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
 
                /* We send data to spare ram with oobsize
                 * to prevent byte access */
                memset(this->page_buf, 0xff, mtd->oobsize);
-               memcpy(this->page_buf + column, buf, thislen);
+               if (mode == MTD_OOB_AUTO)
+                       onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen);
+               else
+                       memcpy(this->page_buf + column, buf, thislen);
                this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
 
                this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1116,26 +1371,25 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
 
                ret = this->wait(mtd, FL_WRITING);
                if (ret) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret);
-                       goto out;
+                       printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret);
+                       break;
                }
 
-               ret = onenand_verify_oob(mtd, buf, to, thislen);
+               ret = onenand_verify_oob(mtd, this->page_buf, to);
                if (ret) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
-                       goto out;
+                       printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret);
+                       break;
                }
 
                written += thislen;
-
                if (written == len)
                        break;
 
-               to += thislen;
+               to += mtd->writesize;
                buf += thislen;
+               column = 0;
        }
 
-out:
        /* Deselect and wake up anyone waiting on the device */
        onenand_release_device(mtd);
 
@@ -1153,10 +1407,17 @@ out:
 static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
                             struct mtd_oob_ops *ops)
 {
-       BUG_ON(ops->mode != MTD_OOB_PLACE);
-
+       switch (ops->mode) {
+       case MTD_OOB_PLACE:
+       case MTD_OOB_AUTO:
+               break;
+       case MTD_OOB_RAW:
+               /* Not implemented yet */
+       default:
+               return -EINVAL;
+       }
        return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
-                                   &ops->oobretlen, ops->oobbuf);
+                                   &ops->oobretlen, ops->oobbuf, ops->mode);
 }
 
 /**
@@ -1199,19 +1460,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 
        /* Start address must align on block boundary */
        if (unlikely(instr->addr & (block_size - 1))) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n");
+               printk(KERN_ERR "onenand_erase: Unaligned address\n");
                return -EINVAL;
        }
 
        /* Length must align on block boundary */
        if (unlikely(instr->len & (block_size - 1))) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n");
+               printk(KERN_ERR "onenand_erase: Length not block aligned\n");
                return -EINVAL;
        }
 
        /* Do not allow erase past end of device */
        if (unlikely((instr->len + instr->addr) > mtd->size)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n");
+               printk(KERN_ERR "onenand_erase: Erase past end of device\n");
                return -EINVAL;
        }
 
@@ -1238,10 +1499,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 
                this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
 
+               onenand_invalidate_bufferram(mtd, addr, block_size);
+
                ret = this->wait(mtd, FL_ERASING);
                /* Check, if it is write protected */
                if (ret) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
+                       printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
                        instr->state = MTD_ERASE_FAILED;
                        instr->fail_addr = addr;
                        goto erase_exit;
@@ -1322,7 +1585,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
 
         /* We write two bytes, so we dont have to mess with 16 bit access */
         ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
-        return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf);
+        return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
 }
 
 /**
@@ -1491,6 +1754,8 @@ static int onenand_unlock_all(struct mtd_info *mtd)
        struct onenand_chip *this = mtd->priv;
 
        if (this->options & ONENAND_HAS_UNLOCK_ALL) {
+               /* Set start block address */
+               this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
                /* Write unlock command */
                this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
 
@@ -1503,13 +1768,10 @@ static int onenand_unlock_all(struct mtd_info *mtd)
                        continue;
 
                /* Workaround for all block unlock in DDP */
-               if (this->device_id & ONENAND_DEVICE_IS_DDP) {
-                       loff_t ofs;
-                       size_t len;
-
+               if (ONENAND_IS_DDP(this)) {
                        /* 1st block on another chip */
-                       ofs = this->chipsize >> 1;
-                       len = 1 << this->erase_shift;
+                       loff_t ofs = this->chipsize >> 1;
+                       size_t len = mtd->erasesize;
 
                        onenand_unlock(mtd, ofs, len);
                }
@@ -1617,7 +1879,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
        this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
        this->wait(mtd, FL_OTPING);
 
-       ret = onenand_do_write_oob(mtd, from, len, retlen, buf);
+       ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
 
        /* Exit OTP access mode */
        this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -1823,12 +2085,13 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 #endif /* CONFIG_MTD_ONENAND_OTP */
 
 /**
- * onenand_lock_scheme - Check and set OneNAND lock scheme
+ * onenand_check_features - Check and set OneNAND features
  * @param mtd          MTD data structure
  *
- * Check and set OneNAND lock scheme
+ * Check and set OneNAND features
+ * - lock scheme
  */
-static void onenand_lock_scheme(struct mtd_info *mtd)
+static void onenand_check_features(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
        unsigned int density, process;
@@ -1961,26 +2224,28 @@ static int onenand_probe(struct mtd_info *mtd)
        density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
        this->chipsize = (16 << density) << 20;
        /* Set density mask. it is used for DDP */
-       this->density_mask = (1 << (density + 6));
+       if (ONENAND_IS_DDP(this))
+               this->density_mask = (1 << (density + 6));
+       else
+               this->density_mask = 0;
 
        /* OneNAND page size & block size */
        /* The data buffer size is equal to page size */
        mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
        mtd->oobsize = mtd->writesize >> 5;
-       /* Pagers per block is always 64 in OneNAND */
+       /* Pages per a block are always 64 in OneNAND */
        mtd->erasesize = mtd->writesize << 6;
 
        this->erase_shift = ffs(mtd->erasesize) - 1;
        this->page_shift = ffs(mtd->writesize) - 1;
-       this->ppb_shift = (this->erase_shift - this->page_shift);
-       this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
+       this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
 
        /* REVIST: Multichip handling */
 
        mtd->size = this->chipsize;
 
-       /* Check OneNAND lock scheme */
-       onenand_lock_scheme(mtd);
+       /* Check OneNAND features */
+       onenand_check_features(mtd);
 
        return 0;
 }
@@ -2021,6 +2286,7 @@ static void onenand_resume(struct mtd_info *mtd)
  */
 int onenand_scan(struct mtd_info *mtd, int maxchips)
 {
+       int i;
        struct onenand_chip *this = mtd->priv;
 
        if (!this->read_word)
@@ -2092,12 +2358,21 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        }
 
        this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+       /*
+        * The number of bytes available for a client to place data into
+        * the out of band area
+        */
+       this->ecclayout->oobavail = 0;
+       for (i = 0; this->ecclayout->oobfree[i].length; i++)
+               this->ecclayout->oobavail +=
+                       this->ecclayout->oobfree[i].length;
+
        mtd->ecclayout = this->ecclayout;
 
        /* Fill in remaining MTD driver data */
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->ecctype = MTD_ECC_SW;
        mtd->erase = onenand_erase;
        mtd->point = NULL;
        mtd->unpoint = NULL;
@@ -2144,8 +2419,11 @@ void onenand_release(struct mtd_info *mtd)
        del_mtd_device (mtd);
 
        /* Free bad block table memory, if allocated */
-       if (this->bbm)
+       if (this->bbm) {
+               struct bbm_info *bbm = this->bbm;
+               kfree(bbm->bbt);
                kfree(this->bbm);
+       }
        /* Buffer allocated by onenand_scan */
        if (this->options & ONENAND_PAGEBUF_ALLOC)
                kfree(this->page_buf);
index 98f8fd1c637510706676b3d5fff7289496f305d0..aecdd50a1781354556c9948ed86b4a1f09e52c36 100644 (file)
@@ -17,8 +17,8 @@
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/compatmac.h>
 
-extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-                              size_t *retlen, u_char *buf);
+extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
+                               struct mtd_oob_ops *ops);
 
 /**
  * check_short_pattern - [GENERIC] check if a pattern is in the buffer
@@ -65,10 +65,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
        int startblock;
        loff_t from;
        size_t readlen, ooblen;
+       struct mtd_oob_ops ops;
 
        printk(KERN_INFO "Scanning device for bad blocks\n");
 
-       len = 1;
+       len = 2;
 
        /* We need only read few bytes from the OOB area */
        scanlen = ooblen = 0;
@@ -82,22 +83,24 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
        startblock = 0;
        from = 0;
 
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooblen = readlen;
+       ops.oobbuf = buf;
+       ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
+
        for (i = startblock; i < numblocks; ) {
                int ret;
 
                for (j = 0; j < len; j++) {
-                       size_t retlen;
-
                        /* No need to read pages fully,
                         * just read required OOB bytes */
-                       ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
-                                                 readlen, &retlen, &buf[0]);
+                       ret = onenand_bbt_read_oob(mtd, from + j * mtd->writesize + bd->offs, &ops);
 
                        /* If it is a initial bad block, just ignore it */
-                       if (ret && !(ret & ONENAND_CTRL_LOAD))
-                               return ret;
+                       if (ret == ONENAND_BBT_READ_FATAL_ERROR)
+                               return -EIO;
 
-                       if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
+                       if (ret || check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
                                bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
                                printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
                                        i >> 1, (unsigned int) from);
@@ -168,8 +171,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
  * marked good / bad blocks and writes the bad block table(s) to
  * the selected place.
  *
- * The bad block table memory is allocated here. It must be freed
- * by calling the onenand_free_bbt function.
+ * The bad block table memory is allocated here. It is freed
+ * by the onenand_release function.
  *
  */
 int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
index 035cd9b0cc08b327f83d8f12958eacdcab7f6c94..a61351f88ec046d894bf07bc042907e2a22bb364 100644 (file)
@@ -94,8 +94,19 @@ static int parse_redboot_partitions(struct mtd_info *master,
                         * (NOTE: this is 'size' not 'data_length'; size is
                         * the full size of the entry.)
                         */
-                       if (swab32(buf[i].size) == master->erasesize) {
+
+                       /* RedBoot can combine the FIS directory and
+                          config partitions into a single eraseblock;
+                          we assume wrong-endian if either the swapped
+                          'size' matches the eraseblock size precisely,
+                          or if the swapped size actually fits in an
+                          eraseblock while the unswapped size doesn't. */
+                       if (swab32(buf[i].size) == master->erasesize ||
+                           (buf[i].size > master->erasesize
+                            && swab32(buf[i].size) < master->erasesize)) {
                                int j;
+                               /* Update numslots based on actual FIS directory size */
+                               numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc);
                                for (j = 0; j < numslots; ++j) {
 
                                        /* A single 0xff denotes a deleted entry.
@@ -120,11 +131,11 @@ static int parse_redboot_partitions(struct mtd_info *master,
                                        swab32s(&buf[j].desc_cksum);
                                        swab32s(&buf[j].file_cksum);
                                }
+                       } else if (buf[i].size < master->erasesize) {
+                               /* Update numslots based on actual FIS directory size */
+                               numslots = buf[i].size / sizeof(struct fis_image_desc);
                        }
                        break;
-               } else {
-                       /* re-calculate of real numslots */
-                       numslots = buf[i].size / sizeof(struct fis_image_desc);
                }
        }
        if (i == numslots) {
index 02826967ab58918ac6fa6ec7723d5e81485c357e..07119c42a861ad2ddf6fa86ae7fc79753879aa0f 100644 (file)
@@ -348,23 +348,27 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
 
        ret = jffs2_sum_init(c);
        if (ret)
-               return ret;
+               goto out_free;
 
        if (jffs2_build_filesystem(c)) {
                dbg_fsbuild("build_fs failed\n");
                jffs2_free_ino_caches(c);
                jffs2_free_raw_node_refs(c);
-#ifndef __ECOS
-               if (jffs2_blocks_use_vmalloc(c))
-                       vfree(c->blocks);
-               else
-#endif
-                       kfree(c->blocks);
-
-               return -EIO;
+               ret = -EIO;
+               goto out_free;
        }
 
        jffs2_calc_trigger_levels(c);
 
        return 0;
+
+ out_free:
+#ifndef __ECOS
+       if (jffs2_blocks_use_vmalloc(c))
+               vfree(c->blocks);
+       else
+#endif
+               kfree(c->blocks);
+
+       return ret;
 }
index b98594992eed530a7431a8db7c85f104ba734d73..ea88f69af13071dcfbb64953609a89678207b0cc 100644 (file)
@@ -98,20 +98,14 @@ struct jffs2_sb_info {
        uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */
 
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-       /* Write-behind buffer for NAND flash */
-       unsigned char *wbuf;
-       unsigned char *oobbuf;
+       unsigned char *wbuf; /* Write-behind buffer for NAND flash */
        uint32_t wbuf_ofs;
        uint32_t wbuf_len;
        struct jffs2_inodirty *wbuf_inodes;
-
        struct rw_semaphore wbuf_sem;   /* Protects the write buffer */
 
-       /* Information about out-of-band area usage... */
-       struct nand_ecclayout *ecclayout;
-       uint32_t badblock_pos;
-       uint32_t fsdata_pos;
-       uint32_t fsdata_len;
+       unsigned char *oobbuf;
+       int oobavail; /* How many bytes are available for JFFS2 in OOB */
 #endif
 
        struct jffs2_summary *summary;          /* Summary information */
index 3af746eaff0ec124168e0baffa247de09253e68c..31c1475d922aa2d3f4eb5c75841838f1100a385b 100644 (file)
@@ -450,16 +450,20 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
 
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
        if (jffs2_cleanmarker_oob(c)) {
-               int ret = jffs2_check_nand_cleanmarker(c, jeb);
+               int ret;
+
+               if (c->mtd->block_isbad(c->mtd, jeb->offset))
+                       return BLK_STATE_BADBLOCK;
+
+               ret = jffs2_check_nand_cleanmarker(c, jeb);
                D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
+
                /* Even if it's not found, we still scan to see
                   if the block is empty. We use this information
                   to decide whether to erase it or not. */
                switch (ret) {
                case 0:         cleanmarkerfound = 1; break;
                case 1:         break;
-               case 2:         return BLK_STATE_BADBLOCK;
-               case 3:         return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
                default:        return ret;
                }
        }
index 9c99859f5eddb12f24386d1cab20f71dd52ef756..de718e3a169296c6c6dc526743f2498762205ee8 100644 (file)
@@ -957,43 +957,48 @@ exit:
        return ret;
 }
 
-#define NR_OOB_SCAN_PAGES      4
+#define NR_OOB_SCAN_PAGES 4
+
+/* For historical reasons we use only 12 bytes for OOB clean marker */
+#define OOB_CM_SIZE 12
+
+static const struct jffs2_unknown_node oob_cleanmarker =
+{
+       .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
+       .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+       .totlen = cpu_to_je32(8)
+};
 
 /*
- * Check, if the out of band area is empty
+ * Check, if the out of band area is empty. This function knows about the clean
+ * marker and if it is present in OOB, treats the OOB as empty anyway.
  */
 int jffs2_check_oob_empty(struct jffs2_sb_info *c,
                          struct jffs2_eraseblock *jeb, int mode)
 {
-       int i, page, ret;
-       int oobsize = c->mtd->oobsize;
+       int i, ret;
+       int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
        struct mtd_oob_ops ops;
 
-       ops.ooblen = NR_OOB_SCAN_PAGES * oobsize;
+       ops.mode = MTD_OOB_AUTO;
+       ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
        ops.oobbuf = c->oobbuf;
-       ops.ooboffs = 0;
+       ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
        ops.datbuf = NULL;
-       ops.mode = MTD_OOB_PLACE;
 
        ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
-       if (ret) {
-               D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
-                         "failed %d for block at %08x\n", ret, jeb->offset));
+       if (ret || ops.oobretlen != ops.ooblen) {
+               printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
+                               " bytes, read %zd bytes, error %d\n",
+                               jeb->offset, ops.ooblen, ops.oobretlen, ret);
+               if (!ret)
+                       ret = -EIO;
                return ret;
        }
 
-       if (ops.oobretlen < ops.ooblen) {
-               D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB "
-                         "returned short read (%zd bytes not %d) for block "
-                         "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset));
-               return -EIO;
-       }
-
-       /* Special check for first page */
-       for(i = 0; i < oobsize ; i++) {
-               /* Yeah, we know about the cleanmarker. */
-               if (mode && i >= c->fsdata_pos &&
-                   i < c->fsdata_pos + c->fsdata_len)
+       for(i = 0; i < ops.ooblen; i++) {
+               if (mode && i < cmlen)
+                       /* Yeah, we know about the cleanmarker */
                        continue;
 
                if (ops.oobbuf[i] != 0xFF) {
@@ -1003,111 +1008,63 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
                }
        }
 
-       /* we know, we are aligned :) */
-       for (page = oobsize; page < ops.ooblen; page += sizeof(long)) {
-               long dat = *(long *)(&ops.oobbuf[page]);
-               if(dat != -1)
-                       return 1;
-       }
        return 0;
 }
 
 /*
- * Scan for a valid cleanmarker and for bad blocks
+ * Check for a valid cleanmarker.
+ * Returns: 0 if a valid cleanmarker was found
+ *          1 if no cleanmarker was found
+ *          negative error code if an error occurred
  */
-int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c,
-                                 struct jffs2_eraseblock *jeb)
+int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
+                                struct jffs2_eraseblock *jeb)
 {
-       struct jffs2_unknown_node n;
        struct mtd_oob_ops ops;
-       int oobsize = c->mtd->oobsize;
-       unsigned char *p,*b;
-       int i, ret;
-       size_t offset = jeb->offset;
-
-       /* Check first if the block is bad. */
-       if (c->mtd->block_isbad(c->mtd, offset)) {
-               D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()"
-                          ": Bad block at %08x\n", jeb->offset));
-               return 2;
-       }
+       int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
 
-       ops.ooblen = oobsize;
+       ops.mode = MTD_OOB_AUTO;
+       ops.ooblen = cmlen;
        ops.oobbuf = c->oobbuf;
-       ops.ooboffs = 0;
+       ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
        ops.datbuf = NULL;
-       ops.mode = MTD_OOB_PLACE;
 
-       ret = c->mtd->read_oob(c->mtd, offset, &ops);
-       if (ret) {
-               D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): "
-                          "Read OOB failed %d for block at %08x\n",
-                          ret, jeb->offset));
+       ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
+       if (ret || ops.oobretlen != ops.ooblen) {
+               printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
+                               " bytes, read %zd bytes, error %d\n",
+                               jeb->offset, ops.ooblen, ops.oobretlen, ret);
+               if (!ret)
+                       ret = -EIO;
                return ret;
        }
 
-       if (ops.oobretlen < ops.ooblen) {
-               D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): "
-                           "Read OOB return short read (%zd bytes not %d) "
-                           "for block at %08x\n", ops.oobretlen, ops.ooblen,
-                           jeb->offset));
-               return -EIO;
-       }
-
-       n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
-       n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
-       n.totlen = cpu_to_je32 (8);
-       p = (unsigned char *) &n;
-       b = c->oobbuf + c->fsdata_pos;
-
-       for (i = c->fsdata_len; i; i--) {
-               if (*b++ != *p++)
-                       ret = 1;
-       }
-
-       D1(if (ret == 1) {
-               printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): "
-                      "Cleanmarker node not detected in block at %08x\n",
-                      offset);
-               printk(KERN_WARNING "OOB at %08zx was ", offset);
-               for (i=0; i < oobsize; i++)
-                       printk("%02x ", c->oobbuf[i]);
-               printk("\n");
-       });
-       return ret;
+       return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen);
 }
 
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
                                 struct jffs2_eraseblock *jeb)
 {
-       struct jffs2_unknown_node n;
-       int     ret;
+       int ret;
        struct mtd_oob_ops ops;
+       int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
 
-       n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
-       n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
-       n.totlen = cpu_to_je32(8);
-
-       ops.ooblen = c->fsdata_len;
-       ops.oobbuf = (uint8_t *)&n;
-       ops.ooboffs = c->fsdata_pos;
+       ops.mode = MTD_OOB_AUTO;
+       ops.ooblen = cmlen;
+       ops.oobbuf = (uint8_t *)&oob_cleanmarker;
+       ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
        ops.datbuf = NULL;
-       ops.mode = MTD_OOB_PLACE;
 
        ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops);
-
-       if (ret) {
-               D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
-                         "Write failed for block at %08x: error %d\n",
-                         jeb->offset, ret));
+       if (ret || ops.oobretlen != ops.ooblen) {
+               printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd"
+                               " bytes, read %zd bytes, error %d\n",
+                               jeb->offset, ops.ooblen, ops.oobretlen, ret);
+               if (!ret)
+                       ret = -EIO;
                return ret;
        }
-       if (ops.oobretlen != ops.ooblen) {
-               D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
-                         "Short write for block at %08x: %zd not %d\n",
-                         jeb->offset, ops.oobretlen, ops.ooblen));
-               return -EIO;
-       }
+
        return 0;
 }
 
@@ -1140,41 +1097,24 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *
        return 1;
 }
 
-static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
+int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
 {
        struct nand_ecclayout *oinfo = c->mtd->ecclayout;
 
-       /* Do this only, if we have an oob buffer */
        if (!c->mtd->oobsize)
                return 0;
 
        /* Cleanmarker is out-of-band, so inline size zero */
        c->cleanmarker_size = 0;
 
-       /* Should we use autoplacement ? */
-       if (!oinfo) {
-               D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
+       if (!oinfo || oinfo->oobavail == 0) {
+               printk(KERN_ERR "inconsistent device description\n");
                return -EINVAL;
        }
 
-       D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
-       /* Get the position of the free bytes */
-       if (!oinfo->oobfree[0].length) {
-               printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep."
-                       " Autoplacement selected and no empty space in oob\n");
-               return -ENOSPC;
-       }
-       c->fsdata_pos = oinfo->oobfree[0].offset;
-       c->fsdata_len = oinfo->oobfree[0].length;
-       if (c->fsdata_len > 8)
-               c->fsdata_len = 8;
+       D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n"));
 
-       return 0;
-}
-
-int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
-{
-       int res;
+       c->oobavail = oinfo->oobavail;
 
        /* Initialise write buffer */
        init_rwsem(&c->wbuf_sem);
@@ -1185,22 +1125,13 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
        if (!c->wbuf)
                return -ENOMEM;
 
-       c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL);
-       if (!c->oobbuf)
-               return -ENOMEM;
-
-       res = jffs2_nand_set_oobinfo(c);
-
-#ifdef BREAKME
-       if (!brokenbuf)
-               brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
-       if (!brokenbuf) {
+       c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL);
+       if (!c->oobbuf) {
                kfree(c->wbuf);
                return -ENOMEM;
        }
-       memset(brokenbuf, 0xdb, c->wbuf_pagesize);
-#endif
-       return res;
+
+       return 0;
 }
 
 void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
index 1221b7c44158e21bfc653876d22c83c37cf0b64c..fff8c53e54341a106592f210fe8fc78bfb170967 100644 (file)
@@ -92,6 +92,13 @@ struct nand_bbt_descr {
  */
 #define ONENAND_BADBLOCK_POS   0
 
+/*
+ * Bad block scanning errors
+ */
+#define ONENAND_BBT_READ_ERROR         1
+#define ONENAND_BBT_READ_ECC_ERROR     2
+#define ONENAND_BBT_READ_FATAL_ERROR   4
+
 /**
  * struct bbm_info - [GENERIC] Bad Block Table data structure
  * @bbt_erase_shift:   [INTERN] number of address bits in a bbt entry
index 28d461d862bda1d042055c463613a1a87338b698..81f3a314dd76dfc6057ef9f7a82fff3b4d08c652 100644 (file)
@@ -183,7 +183,7 @@ typedef union {
 struct map_info {
        char *name;
        unsigned long size;
-       unsigned long phys;
+       resource_size_t phys;
 #define NO_XIP (-1UL)
 
        void __iomem *virt;
index d644e57703ad0ef256b2099af0b883c06959ce35..6a8570be331bbdb3deb587983953485427901ef5 100644 (file)
@@ -85,6 +85,10 @@ typedef enum {
  *             mode = MTD_OOB_PLACE)
  * @datbuf:    data buffer - if NULL only oob data are read/written
  * @oobbuf:    oob data buffer
+ *
+ * Note, it is allowed to read more then one OOB area at one go, but not write.
+ * The interface assumes that the OOB write requests program only one page's
+ * OOB area.
  */
 struct mtd_oob_ops {
        mtd_oob_mode_t  mode;
@@ -117,18 +121,6 @@ struct mtd_info {
        u_int32_t writesize;
 
        u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
-       u_int32_t ecctype;
-       u_int32_t eccsize;
-
-       /*
-        * Reuse some of the above unused fields in the case of NOR flash
-        * with configurable programming regions to avoid modifying the
-        * user visible structure layout/size.  Only valid when the
-        * MTD_PROGRAM_REGIONS flag is set.
-        * (Maybe we should have an union for those?)
-        */
-#define MTD_PROGREGION_CTRLMODE_VALID(mtd)  (mtd)->oobsize
-#define MTD_PROGREGION_CTRLMODE_INVALID(mtd)  (mtd)->ecctype
 
        // Kernel-only stuff starts here.
        char *name;
index 2071b02f0526f6507d9355015939f09029a51586..97523887fe5d22a9d0e2af49142dc62cdb63e806 100644 (file)
@@ -343,6 +343,7 @@ struct nand_buffers {
  * @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
index f775a7af3890087215ce16504a8da68f513310dd..d8af8a95e58d86305227afb9a8385911b4b67b18 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  linux/include/linux/mtd/onenand.h
  *
- *  Copyright (C) 2005-2006 Samsung Electronics
+ *  Copyright (C) 2005-2007 Samsung Electronics
  *  Kyungmin Park <kyungmin.park@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -42,14 +42,10 @@ typedef enum {
 
 /**
  * struct onenand_bufferram - OneNAND BufferRAM Data
- * @block:             block address in BufferRAM
- * @page:              page address in BufferRAM
- * @valid:             valid flag
+ * @blockpage:         block & page address in BufferRAM
  */
 struct onenand_bufferram {
-       int block;
-       int page;
-       int valid;
+       int     blockpage;
 };
 
 /**
@@ -63,7 +59,6 @@ struct onenand_bufferram {
  *                     partly be set to inform onenand_scan about
  * @erase_shift:       [INTERN] number of address bits in a block
  * @page_shift:                [INTERN] number of address bits in a page
- * @ppb_shift:         [INTERN] number of address bits in a pages per block
  * @page_mask:         [INTERN] a page per block mask
  * @bufferram_index:   [INTERN] BufferRAM index
  * @bufferram:         [INTERN] BufferRAM info
@@ -103,7 +98,6 @@ struct onenand_chip {
 
        unsigned int            erase_shift;
        unsigned int            page_shift;
-       unsigned int            ppb_shift;      /* Pages per block shift */
        unsigned int            page_mask;
 
        unsigned int            bufferram_index;
@@ -150,6 +144,9 @@ struct onenand_chip {
 #define ONENAND_SET_SYS_CFG1(v, this)                                  \
        (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1))
 
+#define ONENAND_IS_DDP(this)                                           \
+       (this->device_id & ONENAND_DEVICE_IS_DDP)
+
 /* Check byte access in OneNAND */
 #define ONENAND_CHECK_BYTE_ACCESS(addr)                (addr & 0x1)
 
index e31c8f5d42712601744823dd22c5f31be157f333..af94719890e7676ea090620e6b6b24b50e24407f 100644 (file)
@@ -3,7 +3,8 @@
  *
  *  OneNAND Register header file
  *
- *  Copyright (C) 2005-2006 Samsung Electronics
+ *  Copyright (C) 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #define ONENAND_VERSION_PROCESS_SHIFT  (8)
 
 /*
- * Start Address 1 F100h (R/W)
+ * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W)
  */
 #define ONENAND_DDP_SHIFT              (15)
+#define ONENAND_DDP_CHIP0              (0)
+#define ONENAND_DDP_CHIP1              (1 << ONENAND_DDP_SHIFT)
 
 /*
  * Start Address 8 F107h (R/W)
index 86831e3594f67f7cdf15f6d4570b69c7b6f7caa4..0dc07d5f33545ae37ec667f74ef16a3b593f7ad6 100644 (file)
 #define __LINUX_MTD_PHYSMAP__
 
 #include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
 
+struct map_info;
+
 struct physmap_flash_data {
        unsigned int            width;
        void                    (*set_vpp)(struct map_info *, int);
index f913c30d7b8976754e4c0008eb5c2db8eca44e08..8e501a75a7645cb8d65c811b1bd85c44e406a550 100644 (file)
@@ -36,12 +36,6 @@ struct mtd_oob_buf {
 #define MTD_CAP_NORFLASH       (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
 #define MTD_CAP_NANDFLASH      (MTD_WRITEABLE)
 
-
-// Types of automatic ECC/Checksum available
-#define MTD_ECC_NONE           0       // No automatic ECC available
-#define MTD_ECC_RS_DiskOnChip  1       // Automatic ECC on DiskOnChip
-#define MTD_ECC_SW             2       // SW ECC for Toshiba & Samsung devices
-
 /* ECC byte placement */
 #define MTD_NANDECC_OFF                0       // Switch off ECC (Not recommended)
 #define MTD_NANDECC_PLACE      1       // Use the given placement in the structure (YAFFS1 legacy mode)
@@ -61,6 +55,8 @@ struct mtd_info_user {
        uint32_t erasesize;
        uint32_t writesize;
        uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
+       /* The below two fields are obsolete and broken, do not use them
+        * (TODO: remove at some point) */
        uint32_t ecctype;
        uint32_t eccsize;
 };