Merge git://git.infradead.org/mtd-2.6
Linus Torvalds [Sat, 13 Oct 2007 17:12:15 +0000 (10:12 -0700)]
* git://git.infradead.org/mtd-2.6: (91 commits)
  [MTD] [NAND] Blackfin on-chip NAND Flash Controller driver
  [MTD] [NOR] fix ctrl-alt-del can't reboot for intel flash bug
  [MTD] [NAND] Fix compiler warning in Alauda driver
  [JFFS2] Remove stray debugging printk
  [JFFS2] Handle dirents on the flash with embedded zero bytes in names.
  [JFFS2] Check for creation of dirents with embedded zero bytes in name.
  [JFFS2] Don't count all 'very dirty' blocks except in debug mode
  [JFFS2] Check whether garbage-collection actually obsoleted its victim.
  [JFFS2] Relax threshold for triggering GC due to dirty blocks.
  [MTD] [OneNAND] Fix typo related with recent commit
  [JFFS2] Trigger garbage collection when very_dirty_list size becomes excessive
  [MTD] [NAND] Avoid deadlock in erase callback; release chip lock first.
  [MTD] [NAND] Resume method for CAFÉ NAND controller
  [MTD] [NAND] Fix PCI ident table for CAFÉ NAND controller.
  [MTD] [NAND] s3c2410: fix arch moves
  [MTD] [OneNAND] fix numerous races
  [MTD] map driver for NOR flash on the Intel Vermilion Range chipset
  [JFFS2] Fix unpoint length
  [MTD] fix CFI point method for discontiguous maps
  [MTD] MAPS: Merge Lubbock and Mainstone drivers into common PXA2xx driver
  ...

91 files changed:
Documentation/DocBook/mtdnand.tmpl
drivers/mtd/Kconfig
drivers/mtd/Makefile
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/jedec_probe.c
drivers/mtd/devices/Kconfig
drivers/mtd/devices/Makefile
drivers/mtd/devices/at91_dataflash26.c [deleted file]
drivers/mtd/devices/docprobe.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/devices/pmc551.c
drivers/mtd/inftlmount.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/alchemy-flash.c
drivers/mtd/maps/intel_vr_nor.c [new file with mode: 0644]
drivers/mtd/maps/lubbock-flash.c [deleted file]
drivers/mtd/maps/mainstone-flash.c [deleted file]
drivers/mtd/maps/nettel.c
drivers/mtd/maps/ocelot.c [deleted file]
drivers/mtd/maps/physmap_of.c
drivers/mtd/maps/pmcmsp-flash.c
drivers/mtd/maps/pmcmsp-ramroot.c
drivers/mtd/maps/pq2fads.c [deleted file]
drivers/mtd/maps/pxa2xx-flash.c [new file with mode: 0644]
drivers/mtd/maps/tqm834x.c [deleted file]
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdcore.h [new file with mode: 0644]
drivers/mtd/mtdoops.c [new file with mode: 0644]
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/alauda.c [new file with mode: 0644]
drivers/mtd/nand/bf5xx_nand.c [new file with mode: 0644]
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/diskonchip.c
drivers/mtd/nand/excite_nandflash.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_ids.c
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/ndfc.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/onenand/Kconfig
drivers/mtd/onenand/Makefile
drivers/mtd/onenand/onenand_base.c
drivers/mtd/onenand/onenand_sim.c [new file with mode: 0644]
drivers/mtd/rfd_ftl.c
drivers/mtd/ubi/scan.c
fs/Kconfig
fs/jffs2/Makefile
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jffs2/background.c
fs/jffs2/build.c
fs/jffs2/compr.c
fs/jffs2/compr.h
fs/jffs2/compr_lzo.c [new file with mode: 0644]
fs/jffs2/compr_rtime.c
fs/jffs2/compr_rubin.c
fs/jffs2/compr_zlib.c
fs/jffs2/dir.c
fs/jffs2/erase.c
fs/jffs2/fs.c
fs/jffs2/gc.c
fs/jffs2/jffs2_fs_sb.h
fs/jffs2/nodelist.h
fs/jffs2/nodemgmt.c
fs/jffs2/os-linux.h
fs/jffs2/readinode.c
fs/jffs2/scan.c
fs/jffs2/security.c
fs/jffs2/summary.c
fs/jffs2/summary.h
fs/jffs2/wbuf.c
fs/jffs2/write.c
fs/jffs2/xattr.h
fs/jffs2/xattr_user.c
include/asm-blackfin/mach-bf548/dma.h
include/asm-blackfin/nand.h [new file with mode: 0644]
include/linux/jffs2.h
include/linux/mtd/cfi.h
include/linux/mtd/flashchip.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

index a8c8cce..6fbc41d 100644 (file)
@@ -275,16 +275,13 @@ int __init board_init (void)
        int err = 0;
 
        /* Allocate memory for MTD device structure and private data */
-       board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
+       board_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
        if (!board_mtd) {
                printk ("Unable to allocate NAND MTD device structure.\n");
                err = -ENOMEM;
                goto out;
        }
 
-       /* Initialize structures */
-       memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip));
-
        /* map physical adress */
        baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024);
        if(!baseaddr){
index fbec8cd..8848e8a 100644 (file)
@@ -278,6 +278,14 @@ config SSFDC
          This enables read only access to SmartMedia formatted NAND
          flash. You can mount it with FAT file system.
 
+config MTD_OOPS
+       tristate "Log panic/oops to an MTD buffer"
+       depends on MTD
+       help
+         This enables panic and oops messages to be logged to a circular
+         buffer in a flash partition where it can be read back at some
+         later point.
+
 source "drivers/mtd/chips/Kconfig"
 
 source "drivers/mtd/maps/Kconfig"
index 6d958a4..7f0b04b 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_NFTL)            += nftl.o
 obj-$(CONFIG_INFTL)            += inftl.o
 obj-$(CONFIG_RFD_FTL)          += rfd_ftl.o
 obj-$(CONFIG_SSFDC)            += ssfdc.o
+obj-$(CONFIG_MTD_OOPS)         += mtdoops.o
 
 nftl-objs              := nftlcore.o nftlmount.o
 inftl-objs             := inftlcore.o inftlmount.o
index 2f19fa7..3aa3dca 100644 (file)
@@ -526,7 +526,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
        struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 
        /*
-        * Probing of multi-partition flash ships.
+        * Probing of multi-partition flash chips.
         *
         * To support multiple partitions when available, we simply arrange
         * for each of them to have their own flchip structure even if they
@@ -653,7 +653,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
  resettime:
        timeo = jiffies + HZ;
  retry:
-       if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {
+       if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE || mode == FL_SHUTDOWN)) {
                /*
                 * OK. We have possibility for contension on the write/erase
                 * operations which are global to the real chip and not per
@@ -798,6 +798,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
                if (mode == FL_READY && chip->oldstate == FL_READY)
                        return 0;
 
+       case FL_SHUTDOWN:
+               /* The machine is rebooting now,so no one can get chip anymore */
+               return -EIO;
        default:
        sleep:
                set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1166,28 +1169,34 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si
 {
        struct map_info *map = mtd->priv;
        struct cfi_private *cfi = map->fldrv_priv;
-       unsigned long ofs;
+       unsigned long ofs, last_end = 0;
        int chipnum;
        int ret = 0;
 
        if (!map->virt || (from + len > mtd->size))
                return -EINVAL;
 
-       *mtdbuf = (void *)map->virt + from;
-       *retlen = 0;
-
        /* Now lock the chip(s) to POINT state */
 
        /* ofs: offset within the first chip that the first read should start */
        chipnum = (from >> cfi->chipshift);
        ofs = from - (chipnum << cfi->chipshift);
 
+       *mtdbuf = (void *)map->virt + cfi->chips[chipnum].start + ofs;
+       *retlen = 0;
+
        while (len) {
                unsigned long thislen;
 
                if (chipnum >= cfi->numchips)
                        break;
 
+               /* We cannot point across chips that are virtually disjoint */
+               if (!last_end)
+                       last_end = cfi->chips[chipnum].start;
+               else if (cfi->chips[chipnum].start != last_end)
+                       break;
+
                if ((len + ofs -1) >> cfi->chipshift)
                        thislen = (1<<cfi->chipshift) - ofs;
                else
@@ -1201,6 +1210,7 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si
                len -= thislen;
 
                ofs = 0;
+               last_end += 1 << cfi->chipshift;
                chipnum++;
        }
        return 0;
@@ -1780,7 +1790,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
        return ret;
 }
 
-int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
 {
        unsigned long ofs, len;
        int ret;
@@ -1930,7 +1940,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
        printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
               __FUNCTION__, ofs, len);
        cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
-               ofs, len, 0);
+               ofs, len, NULL);
 #endif
 
        ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
@@ -1940,7 +1950,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
        printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
               __FUNCTION__, ret);
        cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
-               ofs, len, 0);
+               ofs, len, NULL);
 #endif
 
        return ret;
@@ -1954,7 +1964,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
        printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
               __FUNCTION__, ofs, len);
        cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
-               ofs, len, 0);
+               ofs, len, NULL);
 #endif
 
        ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
@@ -1964,7 +1974,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
        printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
               __FUNCTION__, ret);
        cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
-               ofs, len, 0);
+               ofs, len, NULL);
 #endif
 
        return ret;
@@ -2255,7 +2265,7 @@ static void cfi_intelext_save_locks(struct mtd_info *mtd)
                        adr = region->offset + block * len;
 
                        status = cfi_varsize_frob(mtd,
-                                       do_getlockstatus_oneblock, adr, len, 0);
+                                       do_getlockstatus_oneblock, adr, len, NULL);
                        if (status)
                                set_bit(block, region->lockmap);
                        else
@@ -2402,10 +2412,10 @@ static int cfi_intelext_reset(struct mtd_info *mtd)
                   and switch to array mode so any bootloader in
                   flash is accessible for soft reboot. */
                spin_lock(chip->mutex);
-               ret = get_chip(map, chip, chip->start, FL_SYNCING);
+               ret = get_chip(map, chip, chip->start, FL_SHUTDOWN);
                if (!ret) {
                        map_write(map, CMD(0xff), chip->start);
-                       chip->state = FL_READY;
+                       chip->state = FL_SHUTDOWN;
                }
                spin_unlock(chip->mutex);
        }
index 1f64458..389acc6 100644 (file)
@@ -1609,7 +1609,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 }
 
 
-int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
 {
        unsigned long ofs, len;
        int ret;
index 58e561e..a67b23b 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
-#include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -70,6 +69,7 @@
 
 /* Fujitsu */
 #define MBM29F040C     0x00A4
+#define MBM29F800BA    0x2258
 #define MBM29LV650UE   0x22D7
 #define MBM29LV320TE   0x22F6
 #define MBM29LV320BE   0x22F9
 #define LH28F640BF     0x00b0
 
 /* ST - www.st.com */
+#define M29F800AB      0x0058
 #define M29W800DT      0x00D7
 #define M29W800DB      0x005B
 #define M29W160DT      0x22C4
@@ -646,6 +647,23 @@ static const struct amd_flash_info jedec_table[] = {
                }
        }, {
                .mfr_id         = MANUFACTURER_FUJITSU,
+               .dev_id         = MBM29F800BA,
+               .name           = "Fujitsu MBM29F800BA",
+               .uaddr          = {
+                       [0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+                       [1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+               },
+               .DevSize        = SIZE_1MiB,
+               .CmdSet         = P_ID_AMD_STD,
+               .NumEraseRegions= 4,
+               .regions        = {
+                       ERASEINFO(0x04000,1),
+                       ERASEINFO(0x02000,2),
+                       ERASEINFO(0x08000,1),
+                       ERASEINFO(0x10000,15),
+               }
+       }, {
+               .mfr_id         = MANUFACTURER_FUJITSU,
                .dev_id         = MBM29LV650UE,
                .name           = "Fujitsu MBM29LV650UE",
                .uaddr          = {
@@ -1510,6 +1528,23 @@ static const struct amd_flash_info jedec_table[] = {
                        ERASEINFO(0x1000,256)
                }
 
+       }, {
+               .mfr_id         = MANUFACTURER_ST,
+               .dev_id         = M29F800AB,
+               .name           = "ST M29F800AB",
+               .uaddr          = {
+                       [0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+                       [1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+               },
+               .DevSize        = SIZE_1MiB,
+               .CmdSet         = P_ID_AMD_STD,
+               .NumEraseRegions= 4,
+               .regions        = {
+                       ERASEINFO(0x04000,1),
+                       ERASEINFO(0x02000,2),
+                       ERASEINFO(0x08000,1),
+                       ERASEINFO(0x10000,15),
+               }
        }, {
                .mfr_id         = MANUFACTURER_ST,      /* FIXME - CFI device? */
                .dev_id         = M29W800DT,
index ff642f8..811d56f 100644 (file)
@@ -60,21 +60,22 @@ config MTD_DATAFLASH
          Sometimes DataFlash chips are packaged inside MMC-format
          cards; at this writing, the MMC stack won't handle those.
 
-config MTD_DATAFLASH26
-       tristate "AT91RM9200 DataFlash AT26xxx"
-       depends on MTD && ARCH_AT91RM9200 && AT91_SPI
-       help
-         This enables access to the DataFlash chip (AT26xxx) on an
-         AT91RM9200-based board.
-         If you have such a board and such a DataFlash, say 'Y'.
-
 config MTD_M25P80
-       tristate "Support for M25 SPI Flash"
+       tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
        depends on SPI_MASTER && EXPERIMENTAL
        help
-         This enables access to ST M25P80 and similar SPI flash chips,
-         used for program and data storage.  Set up your spi devices
-         with the right board-specific platform data.
+         This enables access to most modern SPI flash chips, used for
+         program and data storage.   Series supported include Atmel AT26DF,
+         Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X.  Other chips
+         are supported as well.  See the driver source for the current list,
+         or to add other chips.
+
+         Note that the original DataFlash chips (AT45 series, not AT26DF),
+         need an entirely different driver.
+
+         Set up your spi devices with the right board-specific platform data,
+         if you want to specify device partitioning or to use a device which
+         doesn't support the JEDEC ID instruction.
 
 config MTD_SLRAM
        tristate "Uncached system RAM"
index 8ab568b..0f788d5 100644 (file)
@@ -16,5 +16,4 @@ obj-$(CONFIG_MTD_MTDRAM)      += mtdram.o
 obj-$(CONFIG_MTD_LART)         += lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)    += block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)    += mtd_dataflash.o
-obj-$(CONFIG_MTD_DATAFLASH26)  += at91_dataflash26.o
 obj-$(CONFIG_MTD_M25P80)       += m25p80.o
diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c
deleted file mode 100644 (file)
index 64ce37f..0000000
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder)
- * This is a largely modified version of at91_dataflash.c that
- * supports AT26xxx dataflash chips. The original driver supports
- * AT45xxx chips.
- *
- * Note: This driver was only tested with an AT26F004. It should be
- * easy to make it work with other AT26xxx dataflash devices, though.
- *
- * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de>
- * original Copyright (C) SAN People (Pty) Ltd
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
-*/
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/mtd/mtd.h>
-
-#include <asm/arch/at91_spi.h>
-
-#define DATAFLASH_MAX_DEVICES  4       /* max number of dataflash devices */
-
-#define MANUFACTURER_ID_ATMEL          0x1F
-
-/* command codes */
-
-#define AT26_OP_READ_STATUS            0x05
-#define AT26_OP_READ_DEV_ID            0x9F
-#define AT26_OP_ERASE_PAGE_4K          0x20
-#define AT26_OP_READ_ARRAY_FAST                0x0B
-#define AT26_OP_SEQUENTIAL_WRITE       0xAF
-#define AT26_OP_WRITE_ENABLE           0x06
-#define AT26_OP_WRITE_DISABLE          0x04
-#define AT26_OP_SECTOR_PROTECT         0x36
-#define AT26_OP_SECTOR_UNPROTECT       0x39
-
-/* status register bits */
-
-#define AT26_STATUS_BUSY               0x01
-#define AT26_STATUS_WRITE_ENABLE       0x02
-
-struct dataflash_local
-{
-       int spi;                        /* SPI chip-select number */
-       unsigned int page_size;         /* number of bytes per page */
-};
-
-
-/* Detected DataFlash devices */
-static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES];
-static int nr_devices = 0;
-
-/* Allocate a single SPI transfer descriptor.  We're assuming that if multiple
-   SPI transfers occur at the same time, spi_access_bus() will serialize them.
-   If this is not valid, then either (i) each dataflash 'priv' structure
-   needs it's own transfer descriptor, (ii) we lock this one, or (iii) use
-   another mechanism.   */
-static struct spi_transfer_list* spi_transfer_desc;
-
-/*
- * Perform a SPI transfer to access the DataFlash device.
- */
-static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len,
-               char* txnext, int txnext_len, char* rxnext, int rxnext_len)
-{
-       struct spi_transfer_list* list = spi_transfer_desc;
-
-       list->tx[0] = tx;       list->txlen[0] = tx_len;
-       list->rx[0] = rx;       list->rxlen[0] = rx_len;
-
-       list->tx[1] = txnext;   list->txlen[1] = txnext_len;
-       list->rx[1] = rxnext;   list->rxlen[1] = rxnext_len;
-
-       list->nr_transfers = nr;
-       /* Note: spi_transfer() always returns 0, there are no error checks */
-       return spi_transfer(list);
-}
-
-/*
- * Return the status of the DataFlash device.
- */
-static unsigned char at91_dataflash26_status(void)
-{
-       unsigned char command[2];
-
-       command[0] = AT26_OP_READ_STATUS;
-       command[1] = 0;
-
-       do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
-
-       return command[1];
-}
-
-/*
- * Poll the DataFlash device until it is READY.
- */
-static unsigned char at91_dataflash26_waitready(void)
-{
-       unsigned char status;
-
-       while (1) {
-               status = at91_dataflash26_status();
-               if (!(status & AT26_STATUS_BUSY))
-                       return status;
-       }
-}
-
-/*
- * Enable/disable write access
- */
- static void at91_dataflash26_write_enable(int enable)
-{
-       unsigned char cmd[2];
-
-       DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable);
-
-       if (enable)
-               cmd[0] = AT26_OP_WRITE_ENABLE;
-       else
-               cmd[0] = AT26_OP_WRITE_DISABLE;
-       cmd[1] = 0;
-
-       do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
-}
-
-/*
- * Protect/unprotect sector
- */
- static void at91_dataflash26_sector_protect(loff_t addr, int protect)
-{
-       unsigned char cmd[4];
-
-       DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n",
-              addr, protect);
-
-       if (protect)
-               cmd[0] = AT26_OP_SECTOR_PROTECT;
-       else
-               cmd[0] = AT26_OP_SECTOR_UNPROTECT;
-       cmd[1] = (addr & 0x00FF0000) >> 16;
-       cmd[2] = (addr & 0x0000FF00) >> 8;
-       cmd[3] = (addr & 0x000000FF);
-
-       do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
-}
-
-/*
- * Erase blocks of flash.
- */
-static int at91_dataflash26_erase(struct mtd_info *mtd,
-                                 struct erase_info *instr)
-{
-       struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
-       unsigned char cmd[4];
-
-       DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n",
-              instr->addr, instr->len);
-
-       /* Sanity checks */
-       if (priv->page_size != 4096)
-               return -EINVAL; /* Can't handle other sizes at the moment */
-
-       if (   ((instr->len % mtd->erasesize) != 0)
-           || ((instr->len % priv->page_size) != 0)
-           || ((instr->addr % priv->page_size) != 0)
-           || ((instr->addr + instr->len) > mtd->size))
-               return -EINVAL;
-
-       spi_access_bus(priv->spi);
-
-       while (instr->len > 0) {
-               at91_dataflash26_write_enable(1);
-               at91_dataflash26_sector_protect(instr->addr, 0);
-               at91_dataflash26_write_enable(1);
-               cmd[0] = AT26_OP_ERASE_PAGE_4K;
-               cmd[1] = (instr->addr & 0x00FF0000) >> 16;
-               cmd[2] = (instr->addr & 0x0000FF00) >> 8;
-               cmd[3] = (instr->addr & 0x000000FF);
-
-               DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x"
-                                       "0x%02x\n",
-                       cmd[0], cmd[1], cmd[2], cmd[3]);
-
-               do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
-               at91_dataflash26_waitready();
-
-               instr->addr += priv->page_size;  /* next page */
-               instr->len -= priv->page_size;
-       }
-
-       at91_dataflash26_write_enable(0);
-       spi_release_bus(priv->spi);
-
-       /* Inform MTD subsystem that erase is complete */
-       instr->state = MTD_ERASE_DONE;
-       if (instr->callback)
-               instr->callback(instr);
-
-       return 0;
-}
-
-/*
- * Read from the DataFlash device.
- *   from   : Start offset in flash device
- *   len    : Number of bytes to read
- *   retlen : Number of bytes actually read
- *   buf    : Buffer that will receive data
- */
-static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len,
-                                size_t *retlen, u_char *buf)
-{
-       struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
-       unsigned char cmd[5];
-
-       DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n",
-             from, from+len);
-
-       *retlen = 0;
-
-       /* Sanity checks */
-       if (!len)
-               return 0;
-       if (from + len > mtd->size)
-               return -EINVAL;
-
-       cmd[0] = AT26_OP_READ_ARRAY_FAST;
-       cmd[1] = (from & 0x00FF0000) >> 16;
-       cmd[2] = (from & 0x0000FF00) >> 8;
-       cmd[3] = (from & 0x000000FF);
-       /* cmd[4] is a "Don't care" byte  */
-
-       DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n",
-              cmd[0], cmd[1], cmd[2], cmd[3]);
-
-       spi_access_bus(priv->spi);
-       do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len);
-       spi_release_bus(priv->spi);
-
-       *retlen = len;
-       return 0;
-}
-
-/*
- * Write to the DataFlash device.
- *   to     : Start offset in flash device
- *   len    : Number of bytes to write
- *   retlen : Number of bytes actually written
- *   buf    : Buffer containing the data
- */
-static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len,
-                                 size_t *retlen, const u_char *buf)
-{
-       struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
-       unsigned int addr, buf_index = 0;
-       int ret = -EIO, sector, last_sector;
-       unsigned char status, cmd[5];
-
-       DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len);
-
-       *retlen = 0;
-
-       /* Sanity checks */
-       if (!len)
-               return 0;
-       if (to + len > mtd->size)
-               return -EINVAL;
-
-       spi_access_bus(priv->spi);
-
-       addr = to;
-       last_sector = -1;
-
-       while (buf_index < len) {
-               sector = addr / priv->page_size;
-               /* Write first byte if a new sector begins */
-               if (sector != last_sector) {
-                       at91_dataflash26_write_enable(1);
-                       at91_dataflash26_sector_protect(addr, 0);
-                       at91_dataflash26_write_enable(1);
-
-                       /* Program first byte of a new sector */
-                       cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
-                       cmd[1] = (addr & 0x00FF0000) >> 16;
-                       cmd[2] = (addr & 0x0000FF00) >> 8;
-                       cmd[3] = (addr & 0x000000FF);
-                       cmd[4] = buf[buf_index++];
-                       do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
-                       status = at91_dataflash26_waitready();
-                       addr++;
-                       /* On write errors, the chip resets the write enable
-                          flag. This also happens after the last byte of a
-                          sector is successfully programmed. */
-                       if (   ( !(status & AT26_STATUS_WRITE_ENABLE))
-                           && ((addr % priv->page_size) != 0) ) {
-                               DEBUG(MTD_DEBUG_LEVEL1,
-                                       "write error1: addr=0x%06x, "
-                                       "status=0x%02x\n", addr, status);
-                               goto write_err;
-                       }
-                       (*retlen)++;
-                       last_sector = sector;
-               }
-
-               /* Write subsequent bytes in the same sector */
-               cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
-               cmd[1] = buf[buf_index++];
-               do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
-               status = at91_dataflash26_waitready();
-               addr++;
-
-               if (   ( !(status & AT26_STATUS_WRITE_ENABLE))
-                   && ((addr % priv->page_size) != 0) ) {
-                       DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, "
-                               "status=0x%02x\n", addr, status);
-                       goto write_err;
-               }
-
-               (*retlen)++;
-       }
-
-       ret = 0;
-       at91_dataflash26_write_enable(0);
-write_err:
-       spi_release_bus(priv->spi);
-       return ret;
-}
-
-/*
- * Initialize and register DataFlash device with MTD subsystem.
- */
-static int __init add_dataflash(int channel, char *name, int nr_pages,
-                               int pagesize)
-{
-       struct mtd_info *device;
-       struct dataflash_local *priv;
-
-       if (nr_devices >= DATAFLASH_MAX_DEVICES) {
-               printk(KERN_ERR "at91_dataflash26: Too many devices "
-                               "detected\n");
-               return 0;
-       }
-
-       device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8,
-                        GFP_KERNEL);
-       if (!device)
-               return -ENOMEM;
-
-       device->name = (char *)&device[1];
-       sprintf(device->name, "%s.spi%d", name, channel);
-       device->size = nr_pages * pagesize;
-       device->erasesize = pagesize;
-       device->owner = THIS_MODULE;
-       device->type = MTD_DATAFLASH;
-       device->flags = MTD_CAP_NORFLASH;
-       device->erase = at91_dataflash26_erase;
-       device->read = at91_dataflash26_read;
-       device->write = at91_dataflash26_write;
-
-       priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local),
-               GFP_KERNEL);
-       if (!priv) {
-               kfree(device);
-               return -ENOMEM;
-       }
-
-       priv->spi = channel;
-       priv->page_size = pagesize;
-       device->priv = priv;
-
-       mtd_devices[nr_devices] = device;
-       nr_devices++;
-       printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n",
-              name, channel, device->size);
-
-       return add_mtd_device(device);
-}
-
-/*
- * Detect and initialize DataFlash device connected to specified SPI channel.
- *
- */
-
-struct dataflash26_types {
-       unsigned char id0;
-       unsigned char id1;
-       char *name;
-       int pagesize;
-       int nr_pages;
-};
-
-struct dataflash26_types df26_types[] = {
-       {
-               .id0 = 0x04,
-               .id1 = 0x00,
-               .name = "AT26F004",
-               .pagesize = 4096,
-               .nr_pages = 128,
-       },
-       {
-               .id0 = 0x45,
-               .id1 = 0x01,
-               .name = "AT26DF081A", /* Not tested ! */
-               .pagesize = 4096,
-               .nr_pages = 256,
-       },
-};
-
-static int __init at91_dataflash26_detect(int channel)
-{
-       unsigned char status, cmd[5];
-       int i;
-
-       spi_access_bus(channel);
-       status = at91_dataflash26_status();
-
-       if (status == 0 || status == 0xff) {
-               printk(KERN_ERR "at91_dataflash26_detect: status error %d\n",
-                       status);
-               spi_release_bus(channel);
-               return -ENODEV;
-       }
-
-       cmd[0] = AT26_OP_READ_DEV_ID;
-       do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
-       spi_release_bus(channel);
-
-       if (cmd[1] != MANUFACTURER_ID_ATMEL)
-               return -ENODEV;
-
-       for (i = 0; i < ARRAY_SIZE(df26_types); i++) {
-               if (   cmd[2] == df26_types[i].id0
-                   && cmd[3] == df26_types[i].id1)
-                       return add_dataflash(channel,
-                                               df26_types[i].name,
-                                               df26_types[i].nr_pages,
-                                               df26_types[i].pagesize);
-       }
-
-       printk(KERN_ERR "at91_dataflash26_detect: Unsupported device "
-                       "(0x%02x/0x%02x)\n", cmd[2], cmd[3]);
-       return -ENODEV;
-}
-
-static int __init at91_dataflash26_init(void)
-{
-       spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list),
-                                   GFP_KERNEL);
-       if (!spi_transfer_desc)
-               return -ENOMEM;
-
-       /* DataFlash (SPI chip select 0) */
-       at91_dataflash26_detect(0);
-
-#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD
-       /* DataFlash card (SPI chip select 3) */
-       at91_dataflash26_detect(3);
-#endif
-       return 0;
-}
-
-static void __exit at91_dataflash26_exit(void)
-{
-       int i;
-
-       for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) {
-               if (mtd_devices[i]) {
-                       del_mtd_device(mtd_devices[i]);
-                       kfree(mtd_devices[i]->priv);
-                       kfree(mtd_devices[i]);
-               }
-       }
-       nr_devices = 0;
-       kfree(spi_transfer_desc);
-}
-
-module_init(at91_dataflash26_init);
-module_exit(at91_dataflash26_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Hans J. Koch");
-MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200");
index 54aa759..d8cc94e 100644 (file)
@@ -81,9 +81,7 @@ static unsigned long __initdata doc_locations[] = {
 #endif /*  CONFIG_MTD_DOCPROBE_HIGH */
 #elif defined(__PPC__)
        0xe4000000,
-#elif defined(CONFIG_MOMENCO_OCELOT_G)
-        0xff000000,
-##else
+#else
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #endif
        0xffffffff };
index 78c2511..98df5bc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * MTD SPI driver for ST M25Pxx flash chips
+ * MTD SPI driver for ST M25Pxx (and similar) serial flash chips
  *
  * Author: Mike Lavender, mike@steroidmicros.com
  *
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
-#include <linux/interrupt.h>
+#include <linux/mutex.h>
+
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
-#include <asm/semaphore.h>
-
-
-/* NOTE: AT 25F and SST 25LF series are very similar,
- * but commands for sector erase and chip id differ...
- */
 
 #define FLASH_PAGESIZE         256
 
 /* Flash opcodes. */
-#define        OPCODE_WREN             6       /* Write enable */
-#define        OPCODE_RDSR             5       /* Read status register */
-#define        OPCODE_READ             3       /* Read data bytes */
-#define        OPCODE_PP               2       /* Page program */
-#define        OPCODE_SE               0xd8    /* Sector erase */
-#define        OPCODE_RES              0xab    /* Read Electronic Signature */
+#define        OPCODE_WREN             0x06    /* Write enable */
+#define        OPCODE_RDSR             0x05    /* Read status register */
+#define        OPCODE_READ             0x03    /* Read data bytes (low frequency) */
+#define        OPCODE_FAST_READ        0x0b    /* Read data bytes (high frequency) */
+#define        OPCODE_PP               0x02    /* Page program (up to 256 bytes) */
+#define        OPCODE_BE_4K            0x20    /* Erase 4KiB block */
+#define        OPCODE_BE_32K           0x52    /* Erase 32KiB block */
+#define        OPCODE_SE               0xd8    /* Sector erase (usually 64KiB) */
 #define        OPCODE_RDID             0x9f    /* Read JEDEC ID */
 
 /* Status Register bits. */
 #define        SR_WIP                  1       /* Write in progress */
 #define        SR_WEL                  2       /* Write enable latch */
+/* meaning of other SR_* bits may differ between vendors */
 #define        SR_BP0                  4       /* Block protect 0 */
 #define        SR_BP1                  8       /* Block protect 1 */
 #define        SR_BP2                  0x10    /* Block protect 2 */
 
 struct m25p {
        struct spi_device       *spi;
-       struct semaphore        lock;
+       struct mutex            lock;
        struct mtd_info         mtd;
-       unsigned                partitioned;
+       unsigned                partitioned:1;
+       u8                      erase_opcode;
        u8                      command[4];
 };
 
@@ -150,8 +150,9 @@ static int wait_till_ready(struct m25p *flash)
  */
 static int erase_sector(struct m25p *flash, u32 offset)
 {
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id,
-                       __FUNCTION__, offset);
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n",
+                       flash->spi->dev.bus_id, __FUNCTION__,
+                       flash->mtd.erasesize / 1024, offset);
 
        /* Wait until finished previous write command. */
        if (wait_till_ready(flash))
@@ -161,7 +162,7 @@ static int erase_sector(struct m25p *flash, u32 offset)
        write_enable(flash);
 
        /* Set up command buffer. */
-       flash->command[0] = OPCODE_SE;
+       flash->command[0] = flash->erase_opcode;
        flash->command[1] = offset >> 16;
        flash->command[2] = offset >> 8;
        flash->command[3] = offset;
@@ -201,13 +202,17 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
        addr = instr->addr;
        len = instr->len;
 
-       down(&flash->lock);
+       mutex_lock(&flash->lock);
+
+       /* REVISIT in some cases we could speed up erasing large regions
+        * by using OPCODE_SE instead of OPCODE_BE_4K
+        */
 
        /* now erase those sectors */
        while (len) {
                if (erase_sector(flash, addr)) {
                        instr->state = MTD_ERASE_FAILED;
-                       up(&flash->lock);
+                       mutex_unlock(&flash->lock);
                        return -EIO;
                }
 
@@ -215,7 +220,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
                len -= mtd->erasesize;
        }
 
-       up(&flash->lock);
+       mutex_unlock(&flash->lock);
 
        instr->state = MTD_ERASE_DONE;
        mtd_erase_callback(instr);
@@ -260,16 +265,19 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
        if (retlen)
                *retlen = 0;
 
-       down(&flash->lock);
+       mutex_lock(&flash->lock);
 
        /* Wait till previous write/erase is done. */
        if (wait_till_ready(flash)) {
                /* REVISIT status return?? */
-               up(&flash->lock);
+               mutex_unlock(&flash->lock);
                return 1;
        }
 
-       /* NOTE:  OPCODE_FAST_READ (if available) is faster... */
+       /* FIXME switch to OPCODE_FAST_READ.  It's required for higher
+        * clocks; and at this writing, every chip this driver handles
+        * supports that opcode.
+        */
 
        /* Set up the write data buffer. */
        flash->command[0] = OPCODE_READ;
@@ -281,7 +289,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 
        *retlen = m.actual_length - sizeof(flash->command);
 
-       up(&flash->lock);
+       mutex_unlock(&flash->lock);
 
        return 0;
 }
@@ -323,7 +331,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
        t[1].tx_buf = buf;
        spi_message_add_tail(&t[1], &m);
 
-       down(&flash->lock);
+       mutex_lock(&flash->lock);
 
        /* Wait until finished previous write command. */
        if (wait_till_ready(flash))
@@ -381,10 +389,10 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
                        if (retlen)
                                *retlen += m.actual_length
                                        - sizeof(flash->command);
-               }
-       }
+               }
+       }
 
-       up(&flash->lock);
+       mutex_unlock(&flash->lock);
 
        return 0;
 }
@@ -398,24 +406,118 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 struct flash_info {
        char            *name;
-       u8              id;
-       u16             jedec_id;
+
+       /* JEDEC id zero means "no ID" (most older chips); otherwise it has
+        * a high byte of zero plus three data bytes: the manufacturer id,
+        * then a two byte device id.
+        */
+       u32             jedec_id;
+
+       /* The size listed here is what works with OPCODE_SE, which isn't
+        * necessarily called a "sector" by the vendor.
+        */
        unsigned        sector_size;
-       unsigned        n_sectors;
+       u16             n_sectors;
+
+       u16             flags;
+#define        SECT_4K         0x01            /* OPCODE_BE_4K works uniformly */
 };
 
+
+/* NOTE: double check command sets and memory organization when you add
+ * more flash chips.  This current list focusses on newer chips, which
+ * have been converging on command sets which including JEDEC ID.
+ */
 static struct flash_info __devinitdata m25p_data [] = {
-       /* REVISIT: fill in JEDEC ids, for parts that have them */
-       { "m25p05", 0x05, 0x2010, 32 * 1024, 2 },
-       { "m25p10", 0x10, 0x2011, 32 * 1024, 4 },
-       { "m25p20", 0x11, 0x2012, 64 * 1024, 4 },
-       { "m25p40", 0x12, 0x2013, 64 * 1024, 8 },
-       { "m25p80", 0x13, 0x0000, 64 * 1024, 16 },
-       { "m25p16", 0x14, 0x2015, 64 * 1024, 32 },
-       { "m25p32", 0x15, 0x2016, 64 * 1024, 64 },
-       { "m25p64", 0x16, 0x2017, 64 * 1024, 128 },
+
+       /* Atmel -- some are (confusingly) marketed as "DataFlash" */
+       { "at25fs010",  0x1f6601, 32 * 1024, 4, SECT_4K, },
+       { "at25fs040",  0x1f6604, 64 * 1024, 8, SECT_4K, },
+
+       { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, },
+
+       { "at26f004",   0x1f0400, 64 * 1024, 8, SECT_4K, },
+       { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, },
+       { "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, },
+       { "at26df321",  0x1f4701, 64 * 1024, 64, SECT_4K, },
+
+       /* Spansion -- single (large) sector size only, at least
+        * for the chips listed here (without boot sectors).
+        */
+       { "s25sl004a", 0x010212, 64 * 1024, 8, },
+       { "s25sl008a", 0x010213, 64 * 1024, 16, },
+       { "s25sl016a", 0x010214, 64 * 1024, 32, },
+       { "s25sl032a", 0x010215, 64 * 1024, 64, },
+       { "s25sl064a", 0x010216, 64 * 1024, 128, },
+
+       /* SST -- large erase sizes are "overlays", "sectors" are 4K */
+       { "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, },
+       { "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, },
+       { "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, },
+       { "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, },
+
+       /* ST Microelectronics -- newer production may have feature updates */
+       { "m25p05",  0x202010,  32 * 1024, 2, },
+       { "m25p10",  0x202011,  32 * 1024, 4, },
+       { "m25p20",  0x202012,  64 * 1024, 4, },
+       { "m25p40",  0x202013,  64 * 1024, 8, },
+       { "m25p80",         0,  64 * 1024, 16, },
+       { "m25p16",  0x202015,  64 * 1024, 32, },
+       { "m25p32",  0x202016,  64 * 1024, 64, },
+       { "m25p64",  0x202017,  64 * 1024, 128, },
+       { "m25p128", 0x202018, 256 * 1024, 64, },
+
+       { "m45pe80", 0x204014,  64 * 1024, 16, },
+       { "m45pe16", 0x204015,  64 * 1024, 32, },
+
+       { "m25pe80", 0x208014,  64 * 1024, 16, },
+       { "m25pe16", 0x208015,  64 * 1024, 32, SECT_4K, },
+
+       /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+       { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, },
+       { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, },
+       { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, },
+       { "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, },
+       { "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, },
+       { "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, },
+       { "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, },
 };
 
+static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+{
+       int                     tmp;
+       u8                      code = OPCODE_RDID;
+       u8                      id[3];
+       u32                     jedec;
+       struct flash_info       *info;
+
+       /* JEDEC also defines an optional "extended device information"
+        * string for after vendor-specific data, after the three bytes
+        * we use here.  Supporting some chips might require using it.
+        */
+       tmp = spi_write_then_read(spi, &code, 1, id, 3);
+       if (tmp < 0) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
+                       spi->dev.bus_id, tmp);
+               return NULL;
+       }
+       jedec = id[0];
+       jedec = jedec << 8;
+       jedec |= id[1];
+       jedec = jedec << 8;
+       jedec |= id[2];
+
+       for (tmp = 0, info = m25p_data;
+                       tmp < ARRAY_SIZE(m25p_data);
+                       tmp++, info++) {
+               if (info->jedec_id == jedec)
+                       return info;
+       }
+       dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
+       return NULL;
+}
+
+
 /*
  * board specific setup should have ensured the SPI clock used here
  * matches what the READ command supports, at least until this driver
@@ -429,37 +531,51 @@ static int __devinit m25p_probe(struct spi_device *spi)
        unsigned                        i;
 
        /* Platform data helps sort out which chip type we have, as
-        * well as how this board partitions it.
+        * well as how this board partitions it.  If we don't have
+        * a chip ID, try the JEDEC id commands; they'll work for most
+        * newer chips, even if we don't recognize the particular chip.
         */
        data = spi->dev.platform_data;
-       if (!data || !data->type) {
-               /* FIXME some chips can identify themselves with RES
-                * or JEDEC get-id commands.  Try them ...
-                */
-               DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n",
-                               spi->dev.bus_id);
-               return -ENODEV;
-       }
+       if (data && data->type) {
+               for (i = 0, info = m25p_data;
+                               i < ARRAY_SIZE(m25p_data);
+                               i++, info++) {
+                       if (strcmp(data->type, info->name) == 0)
+                               break;
+               }
 
-       for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) {
-               if (strcmp(data->type, info->name) == 0)
-                       break;
-       }
-       if (i == ARRAY_SIZE(m25p_data)) {
-               DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n",
-                               spi->dev.bus_id, data->type);
+               /* unrecognized chip? */
+               if (i == ARRAY_SIZE(m25p_data)) {
+                       DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
+                                       spi->dev.bus_id, data->type);
+                       info = NULL;
+
+               /* recognized; is that chip really what's there? */
+               } else if (info->jedec_id) {
+                       struct flash_info       *chip = jedec_probe(spi);
+
+                       if (!chip || chip != info) {
+                               dev_warn(&spi->dev, "found %s, expected %s\n",
+                                               chip ? chip->name : "UNKNOWN",
+                                               info->name);
+                               info = NULL;
+                       }
+               }
+       } else
+               info = jedec_probe(spi);
+
+       if (!info)
                return -ENODEV;
-       }
 
        flash = kzalloc(sizeof *flash, GFP_KERNEL);
        if (!flash)
                return -ENOMEM;
 
        flash->spi = spi;
-       init_MUTEX(&flash->lock);
+       mutex_init(&flash->lock);
        dev_set_drvdata(&spi->dev, flash);
 
-       if (data->name)
+       if (data && data->name)
                flash->mtd.name = data->name;
        else
                flash->mtd.name = spi->dev.bus_id;
@@ -468,17 +584,25 @@ static int __devinit m25p_probe(struct spi_device *spi)
        flash->mtd.writesize = 1;
        flash->mtd.flags = MTD_CAP_NORFLASH;
        flash->mtd.size = info->sector_size * info->n_sectors;
-       flash->mtd.erasesize = info->sector_size;
        flash->mtd.erase = m25p80_erase;
        flash->mtd.read = m25p80_read;
        flash->mtd.write = m25p80_write;
 
+       /* prefer "small sector" erase if possible */
+       if (info->flags & SECT_4K) {
+               flash->erase_opcode = OPCODE_BE_4K;
+               flash->mtd.erasesize = 4096;
+       } else {
+               flash->erase_opcode = OPCODE_SE;
+               flash->mtd.erasesize = info->sector_size;
+       }
+
        dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name,
                        flash->mtd.size / 1024);
 
        DEBUG(MTD_DEBUG_LEVEL2,
-               "mtd .name = %s, .size = 0x%.8x (%uM) "
-                       ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n",
+               "mtd .name = %s, .size = 0x%.8x (%uMiB) "
+                       ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
                flash->mtd.name,
                flash->mtd.size, flash->mtd.size / (1024*1024),
                flash->mtd.erasesize, flash->mtd.erasesize / 1024,
@@ -488,7 +612,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
                for (i = 0; i < flash->mtd.numeraseregions; i++)
                        DEBUG(MTD_DEBUG_LEVEL2,
                                "mtd.eraseregions[%d] = { .offset = 0x%.8x, "
-                               ".erasesize = 0x%.8x (%uK), "
+                               ".erasesize = 0x%.8x (%uKiB), "
                                ".numblocks = %d }\n",
                                i, flash->mtd.eraseregions[i].offset,
                                flash->mtd.eraseregions[i].erasesize,
@@ -516,14 +640,14 @@ static int __devinit m25p_probe(struct spi_device *spi)
                }
 
                if (nr_parts > 0) {
-                       for (i = 0; i < data->nr_parts; i++) {
+                       for (i = 0; i < nr_parts; i++) {
                                DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
                                        "{.name = %s, .offset = 0x%.8x, "
-                                               ".size = 0x%.8x (%uK) }\n",
-                                       i, data->parts[i].name,
-                                       data->parts[i].offset,
-                                       data->parts[i].size,
-                                       data->parts[i].size / 1024);
+                                               ".size = 0x%.8x (%uKiB) }\n",
+                                       i, parts[i].name,
+                                       parts[i].offset,
+                                       parts[i].size,
+                                       parts[i].size / 1024);
                        }
                        flash->partitioned = 1;
                        return add_mtd_partitions(&flash->mtd, parts, nr_parts);
@@ -560,6 +684,11 @@ static struct spi_driver m25p80_driver = {
        },
        .probe  = m25p_probe,
        .remove = __devexit_p(m25p_remove),
+
+       /* REVISIT: many of these chips have deep power-down modes, which
+        * should clearly be entered on suspend() to minimize power use.
+        * And also when they're otherwise idle...
+        */
 };
 
 
index a987e91..a5ed6d2 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/mutex.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
@@ -89,7 +90,7 @@ struct dataflash {
        unsigned short          page_offset;    /* offset in flash address */
        unsigned int            page_size;      /* of bytes per page */
 
-       struct semaphore        lock;
+       struct mutex            lock;
        struct spi_device       *spi;
 
        struct mtd_info         mtd;
@@ -167,7 +168,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
        x.len = 4;
        spi_message_add_tail(&x, &msg);
 
-       down(&priv->lock);
+       mutex_lock(&priv->lock);
        while (instr->len > 0) {
                unsigned int    pageaddr;
                int             status;
@@ -210,7 +211,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
                        instr->len -= priv->page_size;
                }
        }
-       up(&priv->lock);
+       mutex_unlock(&priv->lock);
 
        /* Inform MTD subsystem that erase is complete */
        instr->state = MTD_ERASE_DONE;
@@ -266,7 +267,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
        x[1].len = len;
        spi_message_add_tail(&x[1], &msg);
 
-       down(&priv->lock);
+       mutex_lock(&priv->lock);
 
        /* Continuous read, max clock = f(car) which may be less than
         * the peak rate available.  Some chips support commands with
@@ -279,7 +280,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
        /* plus 4 "don't care" bytes */
 
        status = spi_sync(priv->spi, &msg);
-       up(&priv->lock);
+       mutex_unlock(&priv->lock);
 
        if (status >= 0) {
                *retlen = msg.actual_length - 8;
@@ -336,7 +337,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
        else
                writelen = len;
 
-       down(&priv->lock);
+       mutex_lock(&priv->lock);
        while (remaining > 0) {
                DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n",
                        pageaddr, offset, writelen);
@@ -441,7 +442,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                else
                        writelen = remaining;
        }
-       up(&priv->lock);
+       mutex_unlock(&priv->lock);
 
        return status;
 }
@@ -463,7 +464,7 @@ add_dataflash(struct spi_device *spi, char *name,
        if (!priv)
                return -ENOMEM;
 
-       init_MUTEX(&priv->lock);
+       mutex_init(&priv->lock);
        priv->spi = spi;
        priv->page_size = pagesize;
        priv->page_offset = pageoffset;
index e8f686f..7060a08 100644 (file)
@@ -30,8 +30,8 @@
  *
  * Notes:
  *     Due to what I assume is more buggy SROM, the 64M PMC551 I
- *     have available claims that all 4 of it's DRAM banks have 64M
- *     of ram configured (making a grand total of 256M onboard).
+ *     have available claims that all 4 of its DRAM banks have 64MiB
+ *     of ram configured (making a grand total of 256MiB onboard).
  *     This is slightly annoying since the BAR0 size reflects the
  *     aperture size, not the dram size, and the V370PDC supplies no
  *     other method for memory size discovery.  This problem is
@@ -70,7 +70,7 @@
  *      made the memory unusable, added a fix to code to touch up
  *      the DRAM some.
  *
- * Bugs/FIXME's:
+ * Bugs/FIXMEs:
  *     * MUST fix the init function to not spin on a register
  *     waiting for it to set .. this does not safely handle busted
  *     devices that never reset the register correctly which will
@@ -562,10 +562,10 @@ static u32 fixup_pmc551(struct pci_dev *dev)
        /*
         * Some screen fun
         */
-       printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at "
+       printk(KERN_DEBUG "pmc551: %d%sB (0x%x) of %sprefetchable memory at "
                "0x%llx\n", (size < 1024) ? size : (size < 1048576) ?
                size >> 10 : size >> 20,
-               (size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size,
+               (size < 1024) ? "" : (size < 1048576) ? "Ki" : "Mi", size,
                ((dcmd & (0x1 << 3)) == 0) ? "non-" : "",
                (unsigned long long)pci_resource_start(dev, 0));
 
@@ -649,14 +649,10 @@ MODULE_DESCRIPTION(PMC551_VERSION);
  * Stuff these outside the ifdef so as to not bust compiled in driver support
  */
 static int msize = 0;
-#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE)
-static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE;
-#else
 static int asize = 0;
-#endif
 
 module_param(msize, int, 0);
-MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]");
+MODULE_PARM_DESC(msize, "memory size in MiB [1 - 1024]");
 module_param(asize, int, 0);
 MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]");
 
@@ -799,8 +795,7 @@ static int __init init_pmc551(void)
                mtd->owner = THIS_MODULE;
 
                if (add_mtd_device(mtd)) {
-                       printk(KERN_NOTICE "pmc551: Failed to register new "
-                               "device\n");
+                       printk(KERN_NOTICE "pmc551: Failed to register new device\n");
                        pci_iounmap(PCI_Device, priv->start);
                        kfree(mtd->priv);
                        kfree(mtd);
@@ -811,13 +806,13 @@ static int __init init_pmc551(void)
                pci_dev_get(PCI_Device);
 
                printk(KERN_NOTICE "Registered pmc551 memory device.\n");
-               printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n",
+               printk(KERN_NOTICE "Mapped %dMiB of memory from 0x%p to 0x%p\n",
                        priv->asize >> 20,
                        priv->start, priv->start + priv->asize);
-               printk(KERN_NOTICE "Total memory is %d%c\n",
+               printk(KERN_NOTICE "Total memory is %d%sB\n",
                        (length < 1024) ? length :
                        (length < 1048576) ? length >> 10 : length >> 20,
-                       (length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M');
+                       (length < 1024) ? "" : (length < 1048576) ? "Ki" : "Mi");
                priv->nextpmc551 = pmc551list;
                pmc551list = mtd;
                found++;
@@ -850,7 +845,7 @@ static void __exit cleanup_pmc551(void)
                pmc551list = priv->nextpmc551;
 
                if (priv->start) {
-                       printk(KERN_DEBUG "pmc551: unmapping %dM starting at "
+                       printk(KERN_DEBUG "pmc551: unmapping %dMiB starting at "
                                "0x%p\n", priv->asize >> 20, priv->start);
                        pci_iounmap(priv->dev, priv->start);
                }
index ecac0e4..b8917be 100644 (file)
@@ -580,14 +580,13 @@ int INFTL_mount(struct INFTLrecord *s)
        logical_block = block = BLOCK_NIL;
 
        /* Temporary buffer to store ANAC numbers. */
-       ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL);
+       ANACtable = kcalloc(s->nb_blocks, sizeof(u8), GFP_KERNEL);
        if (!ANACtable) {
                printk(KERN_WARNING "INFTL: allocation of ANACtable "
                                "failed (%zd bytes)\n",
                                s->nb_blocks * sizeof(u8));
                return -ENOMEM;
        }
-       memset(ANACtable, 0, s->nb_blocks);
 
        /*
         * First pass is to explore each physical unit, and construct the
index 6cd132c..2a2a125 100644 (file)
@@ -163,20 +163,12 @@ config MTD_SBC_GXX
          More info at
          <http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>.
 
-config MTD_LUBBOCK
-       tristate "CFI Flash device mapped on Intel Lubbock XScale eval board"
-       depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS
-       help
-         This provides a driver for the on-board flash of the Intel
-         'Lubbock' XScale evaluation board.
-
-config MTD_MAINSTONE
-       tristate "CFI Flash device mapped on Intel Mainstone XScale eval board"
-       depends on MACH_MAINSTONE && MTD_CFI_INTELEXT
+config MTD_PXA2XX
+       tristate "CFI Flash device mapped on Intel XScale PXA2xx based boards"
+       depends on (PXA25x || PXA27x) && MTD_CFI_INTELEXT
        select MTD_PARTITIONS
        help
-         This provides a driver for the on-board flash of the Intel
-         'Mainstone PXA27x evaluation board.
+         This provides a driver for the NOR flash attached to a PXA2xx chip.
 
 config MTD_OCTAGON
        tristate "JEDEC Flash device mapped on Octagon 5066 SBC"
@@ -354,7 +346,7 @@ config MTD_CFI_FLAGADM
 
 config MTD_WALNUT
        tristate "Flash device mapped on IBM 405GP Walnut"
-       depends on MTD_JEDECPROBE && WALNUT
+       depends on MTD_JEDECPROBE && WALNUT && !PPC_MERGE
        help
          This enables access routines for the flash chips on the IBM 405GP
          Walnut board. If you have one of these boards and would like to
@@ -370,7 +362,7 @@ config MTD_EBONY
 
 config MTD_OCOTEA
        tristate "Flash devices mapped on IBM 440GX Ocotea"
-       depends on MTD_CFI && OCOTEA
+       depends on MTD_CFI && OCOTEA && !PPC_MERGE
        help
          This enables access routines for the flash chips on the IBM 440GX
          Ocotea board. If you have one of these boards and would like to
@@ -384,22 +376,6 @@ config MTD_REDWOOD
          Redwood board. If you have one of these boards and would like to
          use the flash chips on it, say 'Y'.
 
-config MTD_TQM834x
-       tristate "Flash device mapped on TQ Components TQM834x Boards"
-       depends on MTD_CFI && TQM834x
-       help
-         This enables access routines for the flash chips on the
-         TQ Components TQM834x boards. If you have one of these boards
-         and would like to use the flash chips on it, say 'Y'.
-
-config MTD_OCELOT
-       tristate "Momenco Ocelot boot flash device"
-       depends on MOMENCO_OCELOT
-       help
-         This enables access routines for the boot flash device and for the
-         NVRAM on the Momenco Ocelot board. If you have one of these boards
-         and would like access to either of these, say 'Y'.
-
 config MTD_SOLUTIONENGINE
        tristate "CFI Flash device mapped on Hitachi SolutionEngine"
        depends on SUPERH && MTD_CFI && MTD_REDBOOT_PARTS
@@ -605,6 +581,13 @@ config MTD_SHARP_SL
        help
          This enables access to the flash chip on the Sharp SL Series of PDAs.
 
+config MTD_INTEL_VR_NOR
+       tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
+       depends on PCI
+       help
+         Map driver for a NOR flash bank located on the Expansion Bus of the
+         Intel Vermilion Range chipset.
+
 config MTD_PLATRAM
        tristate "Map driver for platform device RAM (mtd-ram)"
        select MTD_RAM
index 970b189..316382a 100644 (file)
@@ -20,8 +20,7 @@ obj-$(CONFIG_MTD_ESB2ROM)     += esb2rom.o
 obj-$(CONFIG_MTD_ICHXROM)      += ichxrom.o
 obj-$(CONFIG_MTD_CK804XROM)    += ck804xrom.o
 obj-$(CONFIG_MTD_TSUNAMI)      += tsunami_flash.o
-obj-$(CONFIG_MTD_LUBBOCK)      += lubbock-flash.o
-obj-$(CONFIG_MTD_MAINSTONE)    += mainstone-flash.o
+obj-$(CONFIG_MTD_PXA2XX)       += pxa2xx-flash.o
 obj-$(CONFIG_MTD_MBX860)       += mbx860.o
 obj-$(CONFIG_MTD_CEIVA)                += ceiva.o
 obj-$(CONFIG_MTD_OCTAGON)      += octagon-5066.o
@@ -43,7 +42,6 @@ obj-$(CONFIG_MTD_SUN_UFLASH)  += sun_uflash.o
 obj-$(CONFIG_MTD_VMAX)         += vmax301.o
 obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
 obj-$(CONFIG_MTD_DBOX2)                += dbox2-flash.o
-obj-$(CONFIG_MTD_OCELOT)       += ocelot.o
 obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
 obj-$(CONFIG_MTD_PCI)          += pci.o
 obj-$(CONFIG_MTD_ALCHEMY)       += alchemy-flash.o
@@ -70,4 +68,4 @@ obj-$(CONFIG_MTD_SHARP_SL)    += sharpsl-flash.o
 obj-$(CONFIG_MTD_PLATRAM)      += plat-ram.o
 obj-$(CONFIG_MTD_OMAP_NOR)     += omap_nor.o
 obj-$(CONFIG_MTD_MTX1)         += mtx-1_flash.o
-obj-$(CONFIG_MTD_TQM834x)      += tqm834x.o
+obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
index 84fbe0e..82811bc 100644 (file)
 #define BOARD_FLASH_WIDTH 2 /* 16-bits */
 #endif
 
-#ifdef CONFIG_MIPS_HYDROGEN3
-#define BOARD_MAP_NAME "Hydrogen3 Flash"
-#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
-#define BOARD_FLASH_WIDTH 4 /* 32-bits */
-#define USE_LOCAL_ACCESSORS /* why? */
-#endif
-
 #ifdef CONFIG_MIPS_BOSPORUS
 #define BOARD_MAP_NAME "Bosporus Flash"
 #define BOARD_FLASH_SIZE 0x01000000 /* 16MB */
@@ -130,13 +123,6 @@ int __init alchemy_mtd_init(void)
 
        window_addr = 0x20000000 - BOARD_FLASH_SIZE;
        window_size = BOARD_FLASH_SIZE;
-#ifdef CONFIG_MIPS_MIRAGE_WHY
-       /* Boot ROM flash bank only; no user bank */
-       window_addr = 0x1C000000;
-       window_size = 0x04000000;
-       /* USERFS from 0x1C00 0000 to 0x1FC00000 */
-       alchemy_partitions[0].size = 0x03C00000;
-#endif
 
        /*
         * Static partition definition selection
diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c
new file mode 100644 (file)
index 0000000..1e7814a
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * drivers/mtd/maps/intel_vr_nor.c
+ *
+ * An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel
+ * Vermilion Range chipset.
+ *
+ * The Vermilion Range Expansion Bus supports four chip selects, each of which
+ * has 64MiB of address space.  The 2nd BAR of the Expansion Bus PCI Device
+ * is a 256MiB memory region containing the address spaces for all four of the
+ * chip selects, with start addresses hardcoded on 64MiB boundaries.
+ *
+ * This map driver only supports NOR flash on chip select 0.  The buswidth
+ * (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing
+ * and Control Register for Chip Select 0 (EXP_TIMING_CS0).  This driver does
+ * not modify the value in the EXP_TIMING_CS0 register except to enable writing
+ * and disable boot acceleration.  The timing parameters in the register are
+ * assumed to have been properly initialized by the BIOS.  The reset default
+ * timing parameters are maximally conservative (slow), so access to the flash
+ * will be slower than it should be if the BIOS has not initialized the timing
+ * parameters.
+ *
+ * Author: Andy Lowe <alowe@mvista.com>
+ *
+ * 2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/flashchip.h>
+
+#define DRV_NAME "vr_nor"
+
+struct vr_nor_mtd {
+       void __iomem *csr_base;
+       struct map_info map;
+       struct mtd_info *info;
+       int nr_parts;
+       struct pci_dev *dev;
+};
+
+/* Expansion Bus Configuration and Status Registers are in BAR 0 */
+#define EXP_CSR_MBAR 0
+/* Expansion Bus Memory Window is BAR 1 */
+#define EXP_WIN_MBAR 1
+/* Maximum address space for Chip Select 0 is 64MiB */
+#define CS0_SIZE 0x04000000
+/* Chip Select 0 is at offset 0 in the Memory Window */
+#define CS0_START 0x0
+/* Chip Select 0 Timing Register is at offset 0 in CSR */
+#define EXP_TIMING_CS0 0x00
+#define TIMING_CS_EN           (1 << 31)       /* Chip Select Enable */
+#define TIMING_BOOT_ACCEL_DIS  (1 <<  8)       /* Boot Acceleration Disable */
+#define TIMING_WR_EN           (1 <<  1)       /* Write Enable */
+#define TIMING_BYTE_EN         (1 <<  0)       /* 8-bit vs 16-bit bus */
+#define TIMING_MASK            0x3FFF0000
+
+static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p)
+{
+       if (p->nr_parts > 0) {
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+               del_mtd_partitions(p->info);
+#endif
+       } else
+               del_mtd_device(p->info);
+}
+
+static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p)
+{
+       int err = 0;
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+       struct mtd_partition *parts;
+       static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+       /* register the flash bank */
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+       /* partition the flash bank */
+       p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0);
+       if (p->nr_parts > 0)
+               err = add_mtd_partitions(p->info, parts, p->nr_parts);
+#endif
+       if (p->nr_parts <= 0)
+               err = add_mtd_device(p->info);
+
+       return err;
+}
+
+static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
+{
+       map_destroy(p->info);
+}
+
+static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p)
+{
+       static const char *probe_types[] =
+           { "cfi_probe", "jedec_probe", NULL };
+       const char **type;
+
+       for (type = probe_types; !p->info && *type; type++)
+               p->info = do_map_probe(*type, &p->map);
+       if (!p->info)
+               return -ENODEV;
+
+       p->info->owner = THIS_MODULE;
+
+       return 0;
+}
+
+static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p)
+{
+       unsigned int exp_timing_cs0;
+
+       /* write-protect the flash bank */
+       exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
+       exp_timing_cs0 &= ~TIMING_WR_EN;
+       writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
+
+       /* unmap the flash window */
+       iounmap(p->map.virt);
+
+       /* unmap the csr window */
+       iounmap(p->csr_base);
+}
+
+/*
+ * Initialize the map_info structure and map the flash.
+ * Returns 0 on success, nonzero otherwise.
+ */
+static int __devinit vr_nor_init_maps(struct vr_nor_mtd *p)
+{
+       unsigned long csr_phys, csr_len;
+       unsigned long win_phys, win_len;
+       unsigned int exp_timing_cs0;
+       int err;
+
+       csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR);
+       csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR);
+       win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR);
+       win_len = pci_resource_len(p->dev, EXP_WIN_MBAR);
+
+       if (!csr_phys || !csr_len || !win_phys || !win_len)
+               return -ENODEV;
+
+       if (win_len < (CS0_START + CS0_SIZE))
+               return -ENXIO;
+
+       p->csr_base = ioremap_nocache(csr_phys, csr_len);
+       if (!p->csr_base)
+               return -ENOMEM;
+
+       exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
+       if (!(exp_timing_cs0 & TIMING_CS_EN)) {
+               dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
+                      "is disabled.\n");
+               err = -ENODEV;
+               goto release;
+       }
+       if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) {
+               dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
+                      "is configured for maximally slow access times.\n");
+       }
+       p->map.name = DRV_NAME;
+       p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2;
+       p->map.phys = win_phys + CS0_START;
+       p->map.size = CS0_SIZE;
+       p->map.virt = ioremap_nocache(p->map.phys, p->map.size);
+       if (!p->map.virt) {
+               err = -ENOMEM;
+               goto release;
+       }
+       simple_map_init(&p->map);
+
+       /* Enable writes to flash bank */
+       exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN;
+       writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
+
+       return 0;
+
+      release:
+       iounmap(p->csr_base);
+       return err;
+}
+
+static struct pci_device_id vr_nor_pci_ids[] = {
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},
+       {0,}
+};
+
+static void __devexit vr_nor_pci_remove(struct pci_dev *dev)
+{
+       struct vr_nor_mtd *p = pci_get_drvdata(dev);
+
+       pci_set_drvdata(dev, NULL);
+       vr_nor_destroy_partitions(p);
+       vr_nor_destroy_mtd_setup(p);
+       vr_nor_destroy_maps(p);
+       kfree(p);
+       pci_release_regions(dev);
+       pci_disable_device(dev);
+}
+
+static int __devinit
+vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       struct vr_nor_mtd *p = NULL;
+       unsigned int exp_timing_cs0;
+       int err;
+
+       err = pci_enable_device(dev);
+       if (err)
+               goto out;
+
+       err = pci_request_regions(dev, DRV_NAME);
+       if (err)
+               goto disable_dev;
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       err = -ENOMEM;
+       if (!p)
+               goto release;
+
+       p->dev = dev;
+
+       err = vr_nor_init_maps(p);
+       if (err)
+               goto release;
+
+       err = vr_nor_mtd_setup(p);
+       if (err)
+               goto destroy_maps;
+
+       err = vr_nor_init_partitions(p);
+       if (err)
+               goto destroy_mtd_setup;
+
+       pci_set_drvdata(dev, p);
+
+       return 0;
+
+      destroy_mtd_setup:
+       map_destroy(p->info);
+
+      destroy_maps:
+       /* write-protect the flash bank */
+       exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
+       exp_timing_cs0 &= ~TIMING_WR_EN;
+       writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
+
+       /* unmap the flash window */
+       iounmap(p->map.virt);
+
+       /* unmap the csr window */
+       iounmap(p->csr_base);
+
+      release:
+       kfree(p);
+       pci_release_regions(dev);
+
+      disable_dev:
+       pci_disable_device(dev);
+
+      out:
+       return err;
+}
+
+static struct pci_driver vr_nor_pci_driver = {
+       .name = DRV_NAME,
+       .probe = vr_nor_pci_probe,
+       .remove = __devexit_p(vr_nor_pci_remove),
+       .id_table = vr_nor_pci_ids,
+};
+
+static int __init vr_nor_mtd_init(void)
+{
+       return pci_register_driver(&vr_nor_pci_driver);
+}
+
+static void __exit vr_nor_mtd_exit(void)
+{
+       pci_unregister_driver(&vr_nor_pci_driver);
+}
+
+module_init(vr_nor_mtd_init);
+module_exit(vr_nor_mtd_exit);
+
+MODULE_AUTHOR("Andy Lowe");
+MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids);
diff --git a/drivers/mtd/maps/lubbock-flash.c b/drivers/mtd/maps/lubbock-flash.c
deleted file mode 100644 (file)
index e856068..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * $Id: lubbock-flash.c,v 1.21 2005/11/07 11:14:27 gleixner Exp $
- *
- * Map driver for the Lubbock developer platform.
- *
- * Author:     Nicolas Pitre
- * Copyright:  (C) 2001 MontaVista Software Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/lubbock.h>
-#include <asm/cacheflush.h>
-
-#define ROM_ADDR       0x00000000
-#define FLASH_ADDR     0x04000000
-
-#define WINDOW_SIZE    64*1024*1024
-
-static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len)
-{
-       flush_ioremap_region(map->phys, map->cached, from, len);
-}
-
-static struct map_info lubbock_maps[2] = { {
-       .size =         WINDOW_SIZE,
-       .phys =         0x00000000,
-       .inval_cache =  lubbock_map_inval_cache,
-}, {
-       .size =         WINDOW_SIZE,
-       .phys =         0x04000000,
-       .inval_cache =  lubbock_map_inval_cache,
-} };
-
-static struct mtd_partition lubbock_partitions[] = {
-       {
-               .name =         "Bootloader",
-               .size =         0x00040000,
-               .offset =       0,
-               .mask_flags =   MTD_WRITEABLE  /* force read-only */
-       },{
-               .name =         "Kernel",
-               .size =         0x00100000,
-               .offset =       0x00040000,
-       },{
-               .name =         "Filesystem",
-               .size =         MTDPART_SIZ_FULL,
-               .offset =       0x00140000
-       }
-};
-
-static struct mtd_info *mymtds[2];
-static struct mtd_partition *parsed_parts[2];
-static int nr_parsed_parts[2];
-
-static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-
-static int __init init_lubbock(void)
-{
-       int flashboot = (LUB_CONF_SWITCHES & 1);
-       int ret = 0, i;
-
-       lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth =
-               (BOOT_DEF & 1) ? 2 : 4;
-
-       /* Compensate for the nROMBT switch which swaps the flash banks */
-       printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n",
-              flashboot?"Flash":"ROM", flashboot);
-
-       lubbock_maps[flashboot^1].name = "Lubbock Application Flash";
-       lubbock_maps[flashboot].name = "Lubbock Boot ROM";
-
-       for (i = 0; i < 2; i++) {
-               lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE);
-               if (!lubbock_maps[i].virt) {
-                       printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name);
-                       if (!ret)
-                               ret = -ENOMEM;
-                       continue;
-               }
-               lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE);
-               if (!lubbock_maps[i].cached)
-                       printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name);
-               simple_map_init(&lubbock_maps[i]);
-
-               printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n",
-                      lubbock_maps[i].name, lubbock_maps[i].phys,
-                      lubbock_maps[i].bankwidth * 8);
-
-               mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
-
-               if (!mymtds[i]) {
-                       iounmap((void *)lubbock_maps[i].virt);
-                       if (lubbock_maps[i].cached)
-                               iounmap(lubbock_maps[i].cached);
-                       if (!ret)
-                               ret = -EIO;
-                       continue;
-               }
-               mymtds[i]->owner = THIS_MODULE;
-
-               ret = parse_mtd_partitions(mymtds[i], probes,
-                                          &parsed_parts[i], 0);
-
-               if (ret > 0)
-                       nr_parsed_parts[i] = ret;
-       }
-
-       if (!mymtds[0] && !mymtds[1])
-               return ret;
-
-       for (i = 0; i < 2; i++) {
-               if (!mymtds[i]) {
-                       printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name);
-               } else if (nr_parsed_parts[i]) {
-                       add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]);
-               } else if (!i) {
-                       printk("Using static partitions on %s\n", lubbock_maps[i].name);
-                       add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions));
-               } else {
-                       printk("Registering %s as whole device\n", lubbock_maps[i].name);
-                       add_mtd_device(mymtds[i]);
-               }
-       }
-       return 0;
-}
-
-static void __exit cleanup_lubbock(void)
-{
-       int i;
-       for (i = 0; i < 2; i++) {
-               if (!mymtds[i])
-                       continue;
-
-               if (nr_parsed_parts[i] || !i)
-                       del_mtd_partitions(mymtds[i]);
-               else
-                       del_mtd_device(mymtds[i]);
-
-               map_destroy(mymtds[i]);
-               iounmap((void *)lubbock_maps[i].virt);
-               if (lubbock_maps[i].cached)
-                       iounmap(lubbock_maps[i].cached);
-
-               kfree(parsed_parts[i]);
-       }
-}
-
-module_init(init_lubbock);
-module_exit(cleanup_lubbock);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
-MODULE_DESCRIPTION("MTD map driver for Intel Lubbock");
diff --git a/drivers/mtd/maps/mainstone-flash.c b/drivers/mtd/maps/mainstone-flash.c
deleted file mode 100644 (file)
index d76487d..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * $Id:  $
- *
- * Map driver for the Mainstone developer platform.
- *
- * Author:     Nicolas Pitre
- * Copyright:  (C) 2001 MontaVista Software Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/mainstone.h>
-#include <asm/cacheflush.h>
-
-
-#define ROM_ADDR       0x00000000
-#define FLASH_ADDR     0x04000000
-
-#define WINDOW_SIZE    0x04000000
-
-static void mainstone_map_inval_cache(struct map_info *map, unsigned long from,
-                                     ssize_t len)
-{
-       flush_ioremap_region(map->phys, map->cached, from, len);
-}
-
-static struct map_info mainstone_maps[2] = { {
-       .size =         WINDOW_SIZE,
-       .phys =         PXA_CS0_PHYS,
-       .inval_cache =  mainstone_map_inval_cache,
-}, {
-       .size =         WINDOW_SIZE,
-       .phys =         PXA_CS1_PHYS,
-       .inval_cache =  mainstone_map_inval_cache,
-} };
-
-static struct mtd_partition mainstone_partitions[] = {
-       {
-               .name =         "Bootloader",
-               .size =         0x00040000,
-               .offset =       0,
-               .mask_flags =   MTD_WRITEABLE  /* force read-only */
-       },{
-               .name =         "Kernel",
-               .size =         0x00400000,
-               .offset =       0x00040000,
-       },{
-               .name =         "Filesystem",
-               .size =         MTDPART_SIZ_FULL,
-               .offset =       0x00440000
-       }
-};
-
-static struct mtd_info *mymtds[2];
-static struct mtd_partition *parsed_parts[2];
-static int nr_parsed_parts[2];
-
-static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-
-static int __init init_mainstone(void)
-{
-       int SW7 = 0;  /* FIXME: get from SCR (Mst doc section 3.2.1.1) */
-       int ret = 0, i;
-
-       mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4;
-       mainstone_maps[1].bankwidth = 4;
-
-       /* Compensate for SW7 which swaps the flash banks */
-       mainstone_maps[SW7].name = "processor flash";
-       mainstone_maps[SW7 ^ 1].name = "main board flash";
-
-       printk(KERN_NOTICE "Mainstone configured to boot from %s\n",
-              mainstone_maps[0].name);
-
-       for (i = 0; i < 2; i++) {
-               mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys,
-                                                WINDOW_SIZE);
-               if (!mainstone_maps[i].virt) {
-                       printk(KERN_WARNING "Failed to ioremap %s\n",
-                              mainstone_maps[i].name);
-                       if (!ret)
-                               ret = -ENOMEM;
-                       continue;
-               }
-               mainstone_maps[i].cached =
-                       ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE);
-               if (!mainstone_maps[i].cached)
-                       printk(KERN_WARNING "Failed to ioremap cached %s\n",
-                              mainstone_maps[i].name);
-               simple_map_init(&mainstone_maps[i]);
-
-               printk(KERN_NOTICE
-                      "Probing %s at physical address 0x%08lx"
-                      " (%d-bit bankwidth)\n",
-                      mainstone_maps[i].name, mainstone_maps[i].phys,
-                      mainstone_maps[i].bankwidth * 8);
-
-               mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]);
-
-               if (!mymtds[i]) {
-                       iounmap((void *)mainstone_maps[i].virt);
-                       if (mainstone_maps[i].cached)
-                               iounmap(mainstone_maps[i].cached);
-                       if (!ret)
-                               ret = -EIO;
-                       continue;
-               }
-               mymtds[i]->owner = THIS_MODULE;
-
-               ret = parse_mtd_partitions(mymtds[i], probes,
-                                          &parsed_parts[i], 0);
-
-               if (ret > 0)
-                       nr_parsed_parts[i] = ret;
-       }
-
-       if (!mymtds[0] && !mymtds[1])
-               return ret;
-
-       for (i = 0; i < 2; i++) {
-               if (!mymtds[i]) {
-                       printk(KERN_WARNING "%s is absent. Skipping\n",
-                              mainstone_maps[i].name);
-               } else if (nr_parsed_parts[i]) {
-                       add_mtd_partitions(mymtds[i], parsed_parts[i],
-                                          nr_parsed_parts[i]);
-               } else if (!i) {
-                       printk("Using static partitions on %s\n",
-                              mainstone_maps[i].name);
-                       add_mtd_partitions(mymtds[i], mainstone_partitions,
-                                          ARRAY_SIZE(mainstone_partitions));
-               } else {
-                       printk("Registering %s as whole device\n",
-                              mainstone_maps[i].name);
-                       add_mtd_device(mymtds[i]);
-               }
-       }
-       return 0;
-}
-
-static void __exit cleanup_mainstone(void)
-{
-       int i;
-       for (i = 0; i < 2; i++) {
-               if (!mymtds[i])
-                       continue;
-
-               if (nr_parsed_parts[i] || !i)
-                       del_mtd_partitions(mymtds[i]);
-               else
-                       del_mtd_device(mymtds[i]);
-
-               map_destroy(mymtds[i]);
-               iounmap((void *)mainstone_maps[i].virt);
-               if (mainstone_maps[i].cached)
-                       iounmap(mainstone_maps[i].cached);
-               kfree(parsed_parts[i]);
-       }
-}
-
-module_init(init_mainstone);
-module_exit(cleanup_mainstone);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
-MODULE_DESCRIPTION("MTD map driver for Intel Mainstone");
index 7b96cd0..0c9b305 100644 (file)
@@ -158,68 +158,11 @@ static struct notifier_block nettel_notifier_block = {
        nettel_reboot_notifier, NULL, 0
 };
 
-/*
- *     Erase the configuration file system.
- *     Used to support the software reset button.
- */
-static void nettel_erasecallback(struct erase_info *done)
-{
-       wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
-       wake_up(wait_q);
-}
-
-static struct erase_info nettel_erase;
-
-int nettel_eraseconfig(void)
-{
-       struct mtd_info *mtd;
-       DECLARE_WAITQUEUE(wait, current);
-       wait_queue_head_t wait_q;
-       int ret;
-
-       init_waitqueue_head(&wait_q);
-       mtd = get_mtd_device(NULL, 2);
-       if (!IS_ERR(mtd)) {
-               nettel_erase.mtd = mtd;
-               nettel_erase.callback = nettel_erasecallback;
-               nettel_erase.callback = NULL;
-               nettel_erase.addr = 0;
-               nettel_erase.len = mtd->size;
-               nettel_erase.priv = (u_long) &wait_q;
-               nettel_erase.priv = 0;
-
-               set_current_state(TASK_INTERRUPTIBLE);
-               add_wait_queue(&wait_q, &wait);
-
-               ret = mtd->erase(mtd, &nettel_erase);
-               if (ret) {
-                       set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&wait_q, &wait);
-                       put_mtd_device(mtd);
-                       return(ret);
-               }
-
-               schedule();  /* Wait for erase to finish. */
-               remove_wait_queue(&wait_q, &wait);
-
-               put_mtd_device(mtd);
-       }
-
-       return(0);
-}
-
-#else
-
-int nettel_eraseconfig(void)
-{
-       return(0);
-}
-
 #endif
 
 /****************************************************************************/
 
-int __init nettel_init(void)
+static int __init nettel_init(void)
 {
        volatile unsigned long *amdpar;
        unsigned long amdaddr, maxsize;
@@ -421,10 +364,6 @@ int __init nettel_init(void)
 
        intel_mtd->owner = THIS_MODULE;
 
-#ifndef CONFIG_BLK_DEV_INITRD
-       ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1);
-#endif
-
        num_intel_partitions = sizeof(nettel_intel_partitions) /
                sizeof(nettel_intel_partitions[0]);
 
@@ -477,7 +416,7 @@ out_unmap2:
 
 /****************************************************************************/
 
-void __exit nettel_cleanup(void)
+static void __exit nettel_cleanup(void)
 {
 #ifdef CONFIG_MTD_CFI_INTELEXT
        unregister_reboot_notifier(&nettel_notifier_block);
diff --git a/drivers/mtd/maps/ocelot.c b/drivers/mtd/maps/ocelot.c
deleted file mode 100644 (file)
index 6977963..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * $Id: ocelot.c,v 1.17 2005/11/07 11:14:27 gleixner Exp $
- *
- * Flash on Momenco Ocelot
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#define OCELOT_PLD 0x2c000000
-#define FLASH_WINDOW_ADDR 0x2fc00000
-#define FLASH_WINDOW_SIZE 0x00080000
-#define FLASH_BUSWIDTH 1
-#define NVRAM_WINDOW_ADDR 0x2c800000
-#define NVRAM_WINDOW_SIZE 0x00007FF0
-#define NVRAM_BUSWIDTH 1
-
-static unsigned int cacheflush = 0;
-
-static struct mtd_info *flash_mtd;
-static struct mtd_info *nvram_mtd;
-
-static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
-{
-        struct map_info *map = mtd->priv;
-       size_t done = 0;
-
-       /* If we use memcpy, it does word-wide writes. Even though we told the
-          GT64120A that it's an 8-bit wide region, word-wide writes don't work.
-          We end up just writing the first byte of the four to all four bytes.
-          So we have this loop instead */
-       *retlen = len;
-       while(len) {
-               __raw_writeb(*(unsigned char *) from, map->virt + to);
-               from++;
-               to++;
-               len--;
-       }
-}
-
-static struct mtd_partition *parsed_parts;
-
-struct map_info ocelot_flash_map = {
-       .name = "Ocelot boot flash",
-       .size = FLASH_WINDOW_SIZE,
-       .bankwidth = FLASH_BUSWIDTH,
-       .phys = FLASH_WINDOW_ADDR,
-};
-
-struct map_info ocelot_nvram_map = {
-       .name = "Ocelot NVRAM",
-       .size = NVRAM_WINDOW_SIZE,
-       .bankwidth = NVRAM_BUSWIDTH,
-       .phys = NVRAM_WINDOW_ADDR,
-};
-
-static const char *probes[] = { "RedBoot", NULL };
-
-static int __init init_ocelot_maps(void)
-{
-       void *pld;
-       int nr_parts;
-       unsigned char brd_status;
-
-               printk(KERN_INFO "Momenco Ocelot MTD mappings: Flash 0x%x at 0x%x, NVRAM 0x%x at 0x%x\n",
-              FLASH_WINDOW_SIZE, FLASH_WINDOW_ADDR, NVRAM_WINDOW_SIZE, NVRAM_WINDOW_ADDR);
-
-       /* First check whether the flash jumper is present */
-       pld = ioremap(OCELOT_PLD, 0x10);
-       if (!pld) {
-               printk(KERN_NOTICE "Failed to ioremap Ocelot PLD\n");
-               return -EIO;
-       }
-       brd_status = readb(pld+4);
-       iounmap(pld);
-
-       /* Now ioremap the NVRAM space */
-       ocelot_nvram_map.virt = ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
-       if (!ocelot_nvram_map.virt) {
-               printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n");
-               return -EIO;
-       }
-
-       simple_map_init(&ocelot_nvram_map);
-
-       /* And do the RAM probe on it to get an MTD device */
-       nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map);
-       if (!nvram_mtd) {
-               printk("NVRAM probe failed\n");
-               goto fail_1;
-       }
-       nvram_mtd->owner = THIS_MODULE;
-       nvram_mtd->erasesize = 16;
-       /* Override the write() method */
-       nvram_mtd->write = ocelot_ram_write;
-
-       /* Now map the flash space */
-       ocelot_flash_map.virt = ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
-       if (!ocelot_flash_map.virt) {
-               printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n");
-               goto fail_2;
-       }
-       /* Now the cached version */
-       ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
-
-       simple_map_init(&ocelot_flash_map);
-
-       /* Only probe for flash if the write jumper is present */
-       if (brd_status & 0x40) {
-               flash_mtd = do_map_probe("jedec", &ocelot_flash_map);
-       } else {
-               printk(KERN_NOTICE "Ocelot flash write jumper not present. Treating as ROM\n");
-       }
-       /* If that failed or the jumper's absent, pretend it's ROM */
-       if (!flash_mtd) {
-               flash_mtd = do_map_probe("map_rom", &ocelot_flash_map);
-               /* If we're treating it as ROM, set the erase size */
-               if (flash_mtd)
-                       flash_mtd->erasesize = 0x10000;
-       }
-       if (!flash_mtd)
-               goto fail3;
-
-       add_mtd_device(nvram_mtd);
-
-       flash_mtd->owner = THIS_MODULE;
-       nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
-
-       if (nr_parts > 0)
-               add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
-       else
-               add_mtd_device(flash_mtd);
-
-       return 0;
-
- fail3:
-       iounmap((void *)ocelot_flash_map.virt);
-       if (ocelot_flash_map.cached)
-                       iounmap((void *)ocelot_flash_map.cached);
- fail_2:
-       map_destroy(nvram_mtd);
- fail_1:
-       iounmap((void *)ocelot_nvram_map.virt);
-
-       return -ENXIO;
-}
-
-static void __exit cleanup_ocelot_maps(void)
-{
-       del_mtd_device(nvram_mtd);
-       map_destroy(nvram_mtd);
-       iounmap((void *)ocelot_nvram_map.virt);
-
-       if (parsed_parts)
-               del_mtd_partitions(flash_mtd);
-       else
-               del_mtd_device(flash_mtd);
-       map_destroy(flash_mtd);
-       iounmap((void *)ocelot_flash_map.virt);
-       if (ocelot_flash_map.cached)
-               iounmap((void *)ocelot_flash_map.cached);
-}
-
-module_init(init_ocelot_maps);
-module_exit(cleanup_ocelot_maps);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>");
-MODULE_DESCRIPTION("MTD map driver for Momenco Ocelot board");
index cf75a56..aeed9ea 100644 (file)
@@ -232,7 +232,6 @@ static int __devinit of_flash_probe(struct of_device *dev,
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                goto err_out;
-       memset(info, 0, sizeof(*info));
 
        dev_set_drvdata(&dev->dev, info);
 
index 7e0377e..02bde8c 100644 (file)
@@ -73,13 +73,16 @@ int __init init_msp_flash(void)
                return -ENXIO;
 
        printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
-       msp_flash = (struct mtd_info **)kmalloc(
-                       fcnt * sizeof(struct map_info *), GFP_KERNEL);
-       msp_parts = (struct mtd_partition **)kmalloc(
-                       fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
-       msp_maps = (struct map_info *)kmalloc(
-                       fcnt * sizeof(struct mtd_info), GFP_KERNEL);
-       memset(msp_maps, 0, fcnt * sizeof(struct mtd_info));
+
+       msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
+       msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
+       msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
+       if (!msp_flash || !msp_parts || !msp_maps) {
+               kfree(msp_maps);
+               kfree(msp_parts);
+               kfree(msp_flash);
+               return -ENOMEM;
+       }
 
        /* loop over the flash devices, initializing each */
        for (i = 0; i < fcnt; i++) {
@@ -95,9 +98,8 @@ int __init init_msp_flash(void)
                        continue;
                }
 
-               msp_parts[i] = (struct mtd_partition *)kmalloc(
-                       pcnt * sizeof(struct mtd_partition), GFP_KERNEL);
-               memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition));
+               msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition),
+                                      GFP_KERNEL);
 
                /* now initialize the devices proper */
                flash_name[5] = '0' + i;
index 18049bc..30de5c0 100644 (file)
@@ -79,7 +79,6 @@ static int __init init_rrmap(void)
                rr_mtd->owner = THIS_MODULE;
 
                add_mtd_device(rr_mtd);
-               ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index);
 
                return 0;
        }
diff --git a/drivers/mtd/maps/pq2fads.c b/drivers/mtd/maps/pq2fads.c
deleted file mode 100644 (file)
index fb78d87..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * drivers/mtd/maps/pq2fads.c
- *
- * Mapping for the flash SIMM on 8272ADS and PQ2FADS board
- *
- * Author: Vitaly Bordug <vbordug@ru.mvista.com>
- *
- * 2005 (c) MontaVista Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <asm/ppcboot.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-
-/*
-  NOTE: bank width and interleave relative to the installed flash
-  should have been chosen within MTD_CFI_GEOMETRY options.
-  */
-#define PQ2FADS_BANK_WIDTH 4
-
-static struct mtd_partition pq2fads_partitions[] = {
-       {
-#ifdef CONFIG_ADS8272
-               .name           = "HRCW",
-               .size           = 0x40000,
-               .offset         = 0,
-               .mask_flags     = MTD_WRITEABLE,  /* force read-only */
-       }, {
-               .name           = "User FS",
-               .size           = 0x5c0000,
-               .offset         = 0x40000,
-#else
-               .name           = "User FS",
-               .size           = 0x600000,
-               .offset         = 0,
-#endif
-       }, {
-               .name           = "uImage",
-               .size           = 0x100000,
-               .offset         = 0x600000,
-               .mask_flags     = MTD_WRITEABLE,  /* force read-only */
-       }, {
-               .name           = "bootloader",
-               .size           = 0x40000,
-               .offset         = 0x700000,
-               .mask_flags     = MTD_WRITEABLE,  /* force read-only */
-       }, {
-               .name           = "bootloader env",
-               .size           = 0x40000,
-               .offset         = 0x740000,
-               .mask_flags     = MTD_WRITEABLE,  /* force read-only */
-       }
-};
-
-
-/* pointer to MPC885ADS board info data */
-extern unsigned char __res[];
-
-static int __init init_pq2fads_mtd(void)
-{
-       bd_t *bd = (bd_t *)__res;
-       physmap_configure(bd->bi_flashstart, bd->bi_flashsize, PQ2FADS_BANK_WIDTH, NULL);
-
-       physmap_set_partitions(pq2fads_partitions,
-                               sizeof (pq2fads_partitions) /
-                               sizeof (pq2fads_partitions[0]));
-       return 0;
-}
-
-static void __exit cleanup_pq2fads_mtd(void)
-{
-}
-
-module_init(init_pq2fads_mtd);
-module_exit(cleanup_pq2fads_mtd);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("MTD map and partitions for MPC8272ADS boards");
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c
new file mode 100644 (file)
index 0000000..cb933ac
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Map driver for Intel XScale PXA2xx platforms.
+ *
+ * Author:     Nicolas Pitre
+ * Copyright:  (C) 2001 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include <asm/mach/flash.h>
+
+static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from,
+                                     ssize_t len)
+{
+       consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE);
+}
+
+struct pxa2xx_flash_info {
+       struct mtd_partition    *parts;
+       int                     nr_parts;
+       struct mtd_info         *mtd;
+       struct map_info         map;
+};
+
+
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+
+static int __init pxa2xx_flash_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct flash_platform_data *flash = pdev->dev.platform_data;
+       struct pxa2xx_flash_info *info;
+       struct mtd_partition *parts;
+       struct resource *res;
+       int ret = 0;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+
+       info = kmalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       memset(info, 0, sizeof(struct pxa2xx_flash_info));
+       info->map.name = (char *) flash->name;
+       info->map.bankwidth = flash->width;
+       info->map.phys = res->start;
+       info->map.size = res->end - res->start + 1;
+       info->parts = flash->parts;
+       info->nr_parts = flash->nr_parts;
+
+       info->map.virt = ioremap(info->map.phys, info->map.size);
+       if (!info->map.virt) {
+               printk(KERN_WARNING "Failed to ioremap %s\n",
+                      info->map.name);
+               return -ENOMEM;
+       }
+       info->map.cached =
+               ioremap_cached(info->map.phys, info->map.size);
+       if (!info->map.cached)
+               printk(KERN_WARNING "Failed to ioremap cached %s\n",
+                      info->map.name);
+       info->map.inval_cache = pxa2xx_map_inval_cache;
+       simple_map_init(&info->map);
+
+       printk(KERN_NOTICE
+              "Probing %s at physical address 0x%08lx"
+              " (%d-bit bankwidth)\n",
+              info->map.name, (unsigned long)info->map.phys,
+              info->map.bankwidth * 8);
+
+       info->mtd = do_map_probe(flash->map_name, &info->map);
+
+       if (!info->mtd) {
+               iounmap((void *)info->map.virt);
+               if (info->map.cached)
+                       iounmap(info->map.cached);
+               return -EIO;
+       }
+       info->mtd->owner = THIS_MODULE;
+
+#ifdef CONFIG_MTD_PARTITIONS
+       ret = parse_mtd_partitions(info->mtd, probes, &parts, 0);
+
+       if (ret > 0) {
+               info->nr_parts = ret;
+               info->parts = parts;
+       }
+#endif
+
+       if (info->nr_parts) {
+               add_mtd_partitions(info->mtd, info->parts,
+                                  info->nr_parts);
+       } else {
+               printk("Registering %s as whole device\n",
+                      info->map.name);
+               add_mtd_device(info->mtd);
+       }
+
+       dev_set_drvdata(dev, info);
+       return 0;
+}
+
+static int __exit pxa2xx_flash_remove(struct device *dev)
+{
+       struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
+
+       dev_set_drvdata(dev, NULL);
+
+#ifdef CONFIG_MTD_PARTITIONS
+       if (info->nr_parts)
+               del_mtd_partitions(info->mtd);
+       else
+#endif
+               del_mtd_device(info->mtd);
+
+       map_destroy(info->mtd);
+       iounmap(info->map.virt);
+       if (info->map.cached)
+               iounmap(info->map.cached);
+       kfree(info->parts);
+       kfree(info);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxa2xx_flash_suspend(struct device *dev, pm_message_t state)
+{
+       struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (info->mtd && info->mtd->suspend)
+               ret = info->mtd->suspend(info->mtd);
+       return ret;
+}
+
+static int pxa2xx_flash_resume(struct device *dev)
+{
+       struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
+
+       if (info->mtd && info->mtd->resume)
+               info->mtd->resume(info->mtd);
+       return 0;
+}
+static void pxa2xx_flash_shutdown(struct device *dev)
+{
+       struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
+
+       if (info && info->mtd->suspend(info->mtd) == 0)
+               info->mtd->resume(info->mtd);
+}
+#else
+#define pxa2xx_flash_suspend NULL
+#define pxa2xx_flash_resume NULL
+#define pxa2xx_flash_shutdown NULL
+#endif
+
+static struct device_driver pxa2xx_flash_driver = {
+       .name           = "pxa2xx-flash",
+       .bus            = &platform_bus_type,
+       .probe          = pxa2xx_flash_probe,
+       .remove         = __exit_p(pxa2xx_flash_remove),
+       .suspend        = pxa2xx_flash_suspend,
+       .resume         = pxa2xx_flash_resume,
+       .shutdown       = pxa2xx_flash_shutdown,
+};
+
+static int __init init_pxa2xx_flash(void)
+{
+       return driver_register(&pxa2xx_flash_driver);
+}
+
+static void __exit cleanup_pxa2xx_flash(void)
+{
+       driver_unregister(&pxa2xx_flash_driver);
+}
+
+module_init(init_pxa2xx_flash);
+module_exit(cleanup_pxa2xx_flash);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
+MODULE_DESCRIPTION("MTD map driver for Intel XScale PXA2xx");
diff --git a/drivers/mtd/maps/tqm834x.c b/drivers/mtd/maps/tqm834x.c
deleted file mode 100644 (file)
index 9adc970..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * drivers/mtd/maps/tqm834x.c
- *
- * MTD mapping driver for TQM834x boards
- *
- * Copyright 2005 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>.
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2.  This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <asm/io.h>
-#include <asm/ppcboot.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#define FLASH_BANK_MAX 2
-
-extern unsigned char __res[];
-
-/* trivial struct to describe partition information */
-struct mtd_part_def
-{
-       int nums;
-       unsigned char *type;
-       struct mtd_partition* mtd_part;
-};
-
-static struct mtd_info* mtd_banks[FLASH_BANK_MAX];
-static struct map_info* map_banks[FLASH_BANK_MAX];
-static struct mtd_part_def part_banks[FLASH_BANK_MAX];
-
-static unsigned long num_banks;
-static unsigned long start_scan_addr;
-
-#ifdef CONFIG_MTD_PARTITIONS
-/*
- * The following defines the partition layout of TQM834x boards.
- *
- * See include/linux/mtd/partitions.h for definition of the
- * mtd_partition structure.
- *
- * Assume minimal initial size of 4 MiB per bank, will be updated
- * later in init_tqm834x_mtd() routine.
- */
-
-/* Partition definition for the first flash bank which is always present. */
-static struct mtd_partition tqm834x_partitions_bank1[] = {
-       {
-               .name   = "u-boot",             /* u-boot firmware      */
-               .offset = 0x00000000,
-               .size   = 0x00040000,           /* 256 KiB              */
-               /*mask_flags: MTD_WRITEABLE,     * force read-only      */
-       },
-       {
-               .name   = "env",                /* u-boot environment   */
-               .offset = 0x00040000,
-               .size   = 0x00020000,           /* 128 KiB              */
-               /*mask_flags: MTD_WRITEABLE,     * force read-only      */
-       },
-       {
-               .name   = "kernel",             /* linux kernel image   */
-               .offset = 0x00060000,
-               .size   = 0x00100000,           /* 1 MiB                */
-               /*mask_flags: MTD_WRITEABLE,     * force read-only      */
-       },
-       {
-               .name   = "initrd",             /* ramdisk image        */
-               .offset = 0x00160000,
-               .size   = 0x00200000,           /* 2 MiB                */
-       },
-       {
-               .name   = "user",               /* user data            */
-               .offset = 0x00360000,
-               .size   = 0x000a0000,           /* remaining space      */
-               /* NOTE: this parttion size is re-calcated in           */
-               /* init_tqm834x_mtd() to cover actual remaining space.  */
-       },
-};
-
-/* Partition definition for the second flash bank which may be present on some
- * TQM834x boards.
- */
-static struct mtd_partition tqm834x_partitions_bank2[] = {
-       {
-               .name   = "jffs2",              /* jffs2 filesystem     */
-               .offset = 0x00000000,
-               .size   = 0x00400000,           /* whole device         */
-               /* NOTE: this parttion size is re-calcated in           */
-               /* init_tqm834x_mtd() to cover actual device size.      */
-       },
-};
-
-#endif /* CONFIG_MTD_PARTITIONS */
-
-static int __init init_tqm834x_mtd(void)
-{
-       int idx = 0, ret = 0;
-       unsigned long flash_addr, flash_size, mtd_size = 0;
-
-       /* pointer to TQM834x board info data */
-       bd_t *bd = (bd_t *)__res;
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-       int n;
-       char mtdid[4];
-       const char *part_probes[] = { "cmdlinepart", NULL };
-#endif
-
-       flash_addr = bd->bi_flashstart;
-       flash_size = bd->bi_flashsize;
-
-       /* request maximum flash size address space */
-       start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size);
-       if (!start_scan_addr) {
-               printk("%s: Failed to ioremap address: 0x%lx\n",
-                      __FUNCTION__, flash_addr);
-               return -EIO;
-       }
-
-       for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
-               if (mtd_size >= flash_size)
-                       break;
-
-               pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx);
-
-               map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL);
-               if (map_banks[idx] == NULL) {
-                       ret = -ENOMEM;
-                       goto error_mem;
-               }
-               map_banks[idx]->name = kzalloc(16, GFP_KERNEL);
-               if (map_banks[idx]->name == NULL) {
-                       ret = -ENOMEM;
-                       goto error_mem;
-               }
-
-               sprintf(map_banks[idx]->name, "TQM834x-%d", idx);
-               map_banks[idx]->size = flash_size;
-               map_banks[idx]->bankwidth = 4;
-
-               simple_map_init(map_banks[idx]);
-
-               map_banks[idx]->virt = (void __iomem *)
-                       (start_scan_addr + ((idx > 0) ?
-                       (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0));
-               map_banks[idx]->phys =
-                       flash_addr + ((idx > 0) ?
-                       (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0);
-
-               /* start to probe flash chips */
-               mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]);
-               if (mtd_banks[idx]) {
-                       mtd_banks[idx]->owner = THIS_MODULE;
-                       mtd_size += mtd_banks[idx]->size;
-                       num_banks++;
-                       pr_debug("%s: bank %ld, name: %s, size: %d bytes \n",
-                                __FUNCTION__, num_banks,
-                                mtd_banks[idx]->name, mtd_banks[idx]->size);
-               }
-       }
-
-       /* no supported flash chips found */
-       if (!num_banks) {
-               printk("TQM834x: No supported flash chips found!\n");
-               ret = -ENXIO;
-               goto error_mem;
-       }
-
-#ifdef CONFIG_MTD_PARTITIONS
-       /*
-        * Select static partition definitions
-        */
-       n = ARRAY_SIZE(tqm834x_partitions_bank1);
-       part_banks[0].mtd_part  = tqm834x_partitions_bank1;
-       part_banks[0].type      = "static image bank1";
-       part_banks[0].nums      = n;
-
-       /* update last partition size to cover actual remaining space */
-       tqm834x_partitions_bank1[n - 1].size =
-               mtd_banks[0]->size -
-               tqm834x_partitions_bank1[n - 1].offset;
-
-       /* check if we have second bank? */
-       if (num_banks == 2) {
-               n = ARRAY_SIZE(tqm834x_partitions_bank2);
-               part_banks[1].mtd_part  = tqm834x_partitions_bank2;
-               part_banks[1].type      = "static image bank2";
-               part_banks[1].nums      = n;
-
-               /* update last partition size to cover actual remaining space */
-               tqm834x_partitions_bank2[n - 1].size =
-                       mtd_banks[1]->size -
-                       tqm834x_partitions_bank2[n - 1].offset;
-       }
-
-       for(idx = 0; idx < num_banks ; idx++) {
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-               sprintf(mtdid, "%d", idx);
-               n = parse_mtd_partitions(mtd_banks[idx],
-                                        part_probes,
-                                        &part_banks[idx].mtd_part,
-                                        0);
-               pr_debug("%s: %d command line partitions on bank %s\n",
-                        __FUNCTION__, n, mtdid);
-               if (n > 0) {
-                       part_banks[idx].type = "command line";
-                       part_banks[idx].nums = n;
-               }
-#endif /* CONFIG_MTD_CMDLINE_PARTS */
-               if (part_banks[idx].nums == 0) {
-                       printk(KERN_NOTICE
-                              "TQM834x flash bank %d: no partition info "
-                              "available, registering whole device\n", idx);
-                       add_mtd_device(mtd_banks[idx]);
-               } else {
-                       printk(KERN_NOTICE
-                              "TQM834x flash bank %d: Using %s partition "
-                              "definition\n", idx, part_banks[idx].type);
-                       add_mtd_partitions(mtd_banks[idx],
-                                          part_banks[idx].mtd_part,
-                                          part_banks[idx].nums);
-               }
-       }
-#else  /* ! CONFIG_MTD_PARTITIONS */
-       printk(KERN_NOTICE "TQM834x flash: registering %d flash banks "
-                       "at once\n", num_banks);
-
-       for(idx = 0 ; idx < num_banks ; idx++)
-               add_mtd_device(mtd_banks[idx]);
-
-#endif /* CONFIG_MTD_PARTITIONS */
-
-       return 0;
-error_mem:
-       for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
-               if (map_banks[idx] != NULL) {
-                       if (map_banks[idx]->name != NULL) {
-                               kfree(map_banks[idx]->name);
-                               map_banks[idx]->name = NULL;
-                       }
-                       kfree(map_banks[idx]);
-                       map_banks[idx] = NULL;
-               }
-       }
-
-       iounmap((void *)start_scan_addr);
-
-       return ret;
-}
-
-static void __exit cleanup_tqm834x_mtd(void)
-{
-       unsigned int idx = 0;
-       for(idx = 0 ; idx < num_banks ; idx++) {
-               /* destroy mtd_info previously allocated */
-               if (mtd_banks[idx]) {
-                       del_mtd_partitions(mtd_banks[idx]);
-                       map_destroy(mtd_banks[idx]);
-               }
-
-               /* release map_info not used anymore */
-               kfree(map_banks[idx]->name);
-               kfree(map_banks[idx]);
-       }
-
-       if (start_scan_addr) {
-               iounmap((void *)start_scan_addr);
-               start_scan_addr = 0;
-       }
-}
-
-module_init(init_tqm834x_mtd);
-module_exit(cleanup_tqm834x_mtd);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Wolfgang Denk <wd@denx.de>");
-MODULE_DESCRIPTION("MTD map driver for TQM834x boards");
index ef89780..74d9d30 100644 (file)
 #include <linux/kthread.h>
 #include <asm/uaccess.h>
 
-static LIST_HEAD(blktrans_majors);
+#include "mtdcore.h"
 
-extern struct mutex mtd_table_mutex;
-extern struct mtd_info *mtd_table[];
+static LIST_HEAD(blktrans_majors);
 
 struct mtd_blkcore_priv {
        struct task_struct *thread;
@@ -202,7 +201,7 @@ static int blktrans_ioctl(struct inode *inode, struct file *file,
        }
 }
 
-struct block_device_operations mtd_blktrans_ops = {
+static struct block_device_operations mtd_blktrans_ops = {
        .owner          = THIS_MODULE,
        .open           = blktrans_open,
        .release        = blktrans_release,
index d091b24..22ed96c 100644 (file)
@@ -136,7 +136,8 @@ static int mtd_close(struct inode *inode, struct file *file)
 
        DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 
-       if (mtd->sync)
+       /* Only sync if opened RW */
+       if ((file->f_mode & 2) && mtd->sync)
                mtd->sync(mtd);
 
        put_mtd_device(mtd);
index 41844ea..96be7ef 100644 (file)
@@ -178,7 +178,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
 
        /* Check alignment */
        if (mtd->writesize > 1) {
-               loff_t __to = to;
+               uint64_t __to = to;
                if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
                        return -EINVAL;
        }
index c153b64..6c2645e 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <linux/mtd/mtd.h>
 
+#include "mtdcore.h"
+
 /* These are exported solely for the purpose of mtd_blkdevs.c. You
    should not use them for _anything_ else */
 DEFINE_MUTEX(mtd_table_mutex);
diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
new file mode 100644 (file)
index 0000000..a33251f
--- /dev/null
@@ -0,0 +1,11 @@
+/* linux/drivers/mtd/mtdcore.h
+ *
+ * Header file for driver private mtdcore exports
+ *
+ */
+
+/* These are exported solely for the purpose of mtd_blkdevs.c. You
+   should not use them for _anything_ else */
+
+extern struct mutex mtd_table_mutex;
+extern struct mtd_info *mtd_table[MAX_MTD_DEVICES];
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
new file mode 100644 (file)
index 0000000..f8af627
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * MTD Oops/Panic logger
+ *
+ * Copyright (C) 2007 Nokia Corporation. All rights reserved.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/mtd/mtd.h>
+
+#define OOPS_PAGE_SIZE 4096
+
+static struct mtdoops_context {
+       int mtd_index;
+       struct work_struct work;
+       struct mtd_info *mtd;
+       int oops_pages;
+       int nextpage;
+       int nextcount;
+
+       void *oops_buf;
+       int ready;
+       int writecount;
+} oops_cxt;
+
+static void mtdoops_erase_callback(struct erase_info *done)
+{
+       wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
+       wake_up(wait_q);
+}
+
+static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
+{
+       struct erase_info erase;
+       DECLARE_WAITQUEUE(wait, current);
+       wait_queue_head_t wait_q;
+       int ret;
+
+       init_waitqueue_head(&wait_q);
+       erase.mtd = mtd;
+       erase.callback = mtdoops_erase_callback;
+       erase.addr = offset;
+       if (mtd->erasesize < OOPS_PAGE_SIZE)
+               erase.len = OOPS_PAGE_SIZE;
+       else
+               erase.len = mtd->erasesize;
+       erase.priv = (u_long)&wait_q;
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       add_wait_queue(&wait_q, &wait);
+
+       ret = mtd->erase(mtd, &erase);
+       if (ret) {
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&wait_q, &wait);
+               printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] "
+                                    "on \"%s\" failed\n",
+                       erase.addr, erase.len, mtd->name);
+               return ret;
+       }
+
+       schedule();  /* Wait for erase to finish. */
+       remove_wait_queue(&wait_q, &wait);
+
+       return 0;
+}
+
+static int mtdoops_inc_counter(struct mtdoops_context *cxt)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       size_t retlen;
+       u32 count;
+       int ret;
+
+       cxt->nextpage++;
+       if (cxt->nextpage > cxt->oops_pages)
+               cxt->nextpage = 0;
+       cxt->nextcount++;
+       if (cxt->nextcount == 0xffffffff)
+               cxt->nextcount = 0;
+
+       ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4,
+                       &retlen, (u_char *) &count);
+       if ((retlen != 4) || (ret < 0)) {
+               printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)"
+                               ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE,
+                               retlen, ret);
+               return 1;
+       }
+
+       /* See if we need to erase the next block */
+       if (count != 0xffffffff)
+               return 1;
+
+       printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n",
+                       cxt->nextpage, cxt->nextcount);
+       cxt->ready = 1;
+       return 0;
+}
+
+static void mtdoops_prepare(struct mtdoops_context *cxt)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       int i = 0, j, ret, mod;
+
+       /* We were unregistered */
+       if (!mtd)
+               return;
+
+       mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize;
+       if (mod != 0) {
+               cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE);
+               if (cxt->nextpage > cxt->oops_pages)
+                       cxt->nextpage = 0;
+       }
+
+       while (mtd->block_isbad &&
+                       mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) {
+badblock:
+               printk(KERN_WARNING "mtdoops: Bad block at %08x\n",
+                               cxt->nextpage * OOPS_PAGE_SIZE);
+               i++;
+               cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE);
+               if (cxt->nextpage > cxt->oops_pages)
+                       cxt->nextpage = 0;
+               if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) {
+                       printk(KERN_ERR "mtdoops: All blocks bad!\n");
+                       return;
+               }
+       }
+
+       for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
+               ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
+
+       if (ret < 0) {
+               if (mtd->block_markbad)
+                       mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
+               goto badblock;
+       }
+
+       printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount);
+
+       cxt->ready = 1;
+}
+
+static void mtdoops_workfunc(struct work_struct *work)
+{
+       struct mtdoops_context *cxt =
+                       container_of(work, struct mtdoops_context, work);
+
+       mtdoops_prepare(cxt);
+}
+
+static int find_next_position(struct mtdoops_context *cxt)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       int page, maxpos = 0;
+       u32 count, maxcount = 0xffffffff;
+       size_t retlen;
+
+       for (page = 0; page < cxt->oops_pages; page++) {
+               mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count);
+               if (count == 0xffffffff)
+                       continue;
+               if (maxcount == 0xffffffff) {
+                       maxcount = count;
+                       maxpos = page;
+               } else if ((count < 0x40000000) && (maxcount > 0xc0000000)) {
+                       maxcount = count;
+                       maxpos = page;
+               } else if ((count > maxcount) && (count < 0xc0000000)) {
+                       maxcount = count;
+                       maxpos = page;
+               } else if ((count > maxcount) && (count > 0xc0000000)
+                                       && (maxcount > 0x80000000)) {
+                       maxcount = count;
+                       maxpos = page;
+               }
+       }
+       if (maxcount == 0xffffffff) {
+               cxt->nextpage = 0;
+               cxt->nextcount = 1;
+               cxt->ready = 1;
+               printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n",
+                               cxt->nextpage, cxt->nextcount);
+               return 0;
+       }
+
+       cxt->nextpage = maxpos;
+       cxt->nextcount = maxcount;
+
+       return mtdoops_inc_counter(cxt);
+}
+
+
+static void mtdoops_notify_add(struct mtd_info *mtd)
+{
+       struct mtdoops_context *cxt = &oops_cxt;
+       int ret;
+
+       if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
+               return;
+
+       if (mtd->size < (mtd->erasesize * 2)) {
+               printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n",
+                               mtd->index);
+               return;
+       }
+
+       cxt->mtd = mtd;
+       cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE;
+
+       ret = find_next_position(cxt);
+       if (ret == 1)
+               mtdoops_prepare(cxt);
+
+       printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index);
+}
+
+static void mtdoops_notify_remove(struct mtd_info *mtd)
+{
+       struct mtdoops_context *cxt = &oops_cxt;
+
+       if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
+               return;
+
+       cxt->mtd = NULL;
+       flush_scheduled_work();
+}
+
+static void mtdoops_console_sync(void)
+{
+       struct mtdoops_context *cxt = &oops_cxt;
+       struct mtd_info *mtd = cxt->mtd;
+       size_t retlen;
+       int ret;
+
+       if (!cxt->ready || !mtd)
+               return;
+
+       if (cxt->writecount == 0)
+               return;
+
+       if (cxt->writecount < OOPS_PAGE_SIZE)
+               memset(cxt->oops_buf + cxt->writecount, 0xff,
+                                       OOPS_PAGE_SIZE - cxt->writecount);
+
+       ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE,
+                                       OOPS_PAGE_SIZE, &retlen, cxt->oops_buf);
+       cxt->ready = 0;
+       cxt->writecount = 0;
+
+       if ((retlen != OOPS_PAGE_SIZE) || (ret < 0))
+               printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n",
+                       cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret);
+
+       ret = mtdoops_inc_counter(cxt);
+       if (ret == 1)
+               schedule_work(&cxt->work);
+}
+
+static void
+mtdoops_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct mtdoops_context *cxt = co->data;
+       struct mtd_info *mtd = cxt->mtd;
+       int i;
+
+       if (!oops_in_progress) {
+               mtdoops_console_sync();
+               return;
+       }
+
+       if (!cxt->ready || !mtd)
+               return;
+
+       if (cxt->writecount == 0) {
+               u32 *stamp = cxt->oops_buf;
+               *stamp = cxt->nextcount;
+               cxt->writecount = 4;
+       }
+
+       if ((count + cxt->writecount) > OOPS_PAGE_SIZE)
+               count = OOPS_PAGE_SIZE - cxt->writecount;
+
+       for (i = 0; i < count; i++, s++)
+               *((char *)(cxt->oops_buf) + cxt->writecount + i) = *s;
+
+       cxt->writecount = cxt->writecount + count;
+}
+
+static int __init mtdoops_console_setup(struct console *co, char *options)
+{
+       struct mtdoops_context *cxt = co->data;
+
+       if (cxt->mtd_index != -1)
+               return -EBUSY;
+       if (co->index == -1)
+               return -EINVAL;
+
+       cxt->mtd_index = co->index;
+       return 0;
+}
+
+static struct mtd_notifier mtdoops_notifier = {
+       .add    = mtdoops_notify_add,
+       .remove = mtdoops_notify_remove,
+};
+
+static struct console mtdoops_console = {
+       .name           = "ttyMTD",
+       .write          = mtdoops_console_write,
+       .setup          = mtdoops_console_setup,
+       .unblank        = mtdoops_console_sync,
+       .flags          = CON_PRINTBUFFER,
+       .index          = -1,
+       .data           = &oops_cxt,
+};
+
+static int __init mtdoops_console_init(void)
+{
+       struct mtdoops_context *cxt = &oops_cxt;
+
+       cxt->mtd_index = -1;
+       cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE);
+
+       if (!cxt->oops_buf) {
+               printk(KERN_ERR "Failed to allocate oops buffer workspace\n");
+               return -ENOMEM;
+       }
+
+       INIT_WORK(&cxt->work, mtdoops_workfunc);
+
+       register_console(&mtdoops_console);
+       register_mtd_user(&mtdoops_notifier);
+       return 0;
+}
+
+static void __exit mtdoops_console_exit(void)
+{
+       struct mtdoops_context *cxt = &oops_cxt;
+
+       unregister_mtd_user(&mtdoops_notifier);
+       unregister_console(&mtdoops_console);
+       vfree(cxt->oops_buf);
+}
+
+
+subsys_initcall(mtdoops_console_init);
+module_exit(mtdoops_console_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver");
index f1d60b6..8f9c3ba 100644 (file)
@@ -91,6 +91,25 @@ config MTD_NAND_AU1550
          This enables the driver for the NAND flash controller on the
          AMD/Alchemy 1550 SOC.
 
+config MTD_NAND_BF5XX
+       tristate "Blackfin on-chip NAND Flash Controller driver"
+       depends on BF54x && MTD_NAND
+       help
+         This enables the Blackfin on-chip NAND flash controller
+
+         No board specific support is done by this driver, each board
+         must advertise a platform_device for the driver to attach.
+
+         This driver can also be built as a module. If so, the module
+         will be called bf5xx-nand.
+
+config MTD_NAND_BF5XX_HWECC
+       bool "BF5XX NAND Hardware ECC"
+       depends on MTD_NAND_BF5XX
+       help
+         Enable the use of the BF5XX's internal ECC generator when
+         using NAND.
+
 config MTD_NAND_RTC_FROM4
        tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
        depends on SH_SOLUTION_ENGINE
@@ -134,10 +153,10 @@ config MTD_NAND_S3C2410_HWECC
 
 config MTD_NAND_NDFC
        tristate "NDFC NanD Flash Controller"
-       depends on 44x
+       depends on 4xx && !PPC_MERGE
        select MTD_NAND_ECC_SMC
        help
-        NDFC Nand Flash Controllers are integrated in EP44x SoCs
+        NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
 
 config MTD_NAND_S3C2410_CLKSTOP
        bool "S3C2410 NAND IDLE clock stop"
@@ -237,7 +256,7 @@ config MTD_NAND_CAFE
        select REED_SOLOMON
        select REED_SOLOMON_DEC16
        help
-         Use NAND flash attached to the CAFÉ chip designed for the $100
+         Use NAND flash attached to the CAFÉ chip designed for the OLPC
          laptop.
 
 config MTD_NAND_CS553X
@@ -280,5 +299,11 @@ config MTD_NAND_PLATFORM
          devices. You will need to provide platform-specific functions
          via platform_data.
 
+config MTD_ALAUDA
+       tristate "MTD driver for Olympus MAUSB-10 and Fijufilm DPC-R1"
+       depends on MTD_NAND && USB
+       help
+         These two (and possibly other) Alauda-based cardreaders for
+         SmartMedia and xD allow raw flash access.
 
 endif # MTD_NAND
index edba1db..3ad6c01 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_NAND_TOTO)           += toto.o
 obj-$(CONFIG_MTD_NAND_AUTCPU12)                += autcpu12.o
 obj-$(CONFIG_MTD_NAND_EDB7312)         += edb7312.o
 obj-$(CONFIG_MTD_NAND_AU1550)          += au1550nd.o
+obj-$(CONFIG_MTD_NAND_BF5XX)           += bf5xx_nand.o
 obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB)  += ppchameleonevb.o
 obj-$(CONFIG_MTD_NAND_S3C2410)         += s3c2410.o
 obj-$(CONFIG_MTD_NAND_DISKONCHIP)      += diskonchip.o
@@ -27,5 +28,6 @@ obj-$(CONFIG_MTD_NAND_AT91)           += at91_nand.o
 obj-$(CONFIG_MTD_NAND_CM_X270)         += cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)   += excite_nandflash.o
 obj-$(CONFIG_MTD_NAND_PLATFORM)                += plat_nand.o
+obj-$(CONFIG_MTD_ALAUDA)               += alauda.o
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c
new file mode 100644 (file)
index 0000000..257937c
--- /dev/null
@@ -0,0 +1,742 @@
+/*
+ * MTD driver for Alauda chips
+ *
+ * Copyright (C) 2007 Joern Engel <joern@logfs.org>
+ *
+ * Based on drivers/usb/usb-skeleton.c which is:
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * and on drivers/usb/storage/alauda.c, which is:
+ *   (c) 2005 Daniel Drake <dsd@gentoo.org>
+ *
+ * Idea and initial work by Arnd Bergmann <arnd@arndb.de>
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand_ecc.h>
+
+/* Control commands */
+#define ALAUDA_GET_XD_MEDIA_STATUS     0x08
+#define ALAUDA_ACK_XD_MEDIA_CHANGE     0x0a
+#define ALAUDA_GET_XD_MEDIA_SIG                0x86
+
+/* Common prefix */
+#define ALAUDA_BULK_CMD                        0x40
+
+/* The two ports */
+#define ALAUDA_PORT_XD                 0x00
+#define ALAUDA_PORT_SM                 0x01
+
+/* Bulk commands */
+#define ALAUDA_BULK_READ_PAGE          0x84
+#define ALAUDA_BULK_READ_OOB           0x85 /* don't use, there's a chip bug */
+#define ALAUDA_BULK_READ_BLOCK         0x94
+#define ALAUDA_BULK_ERASE_BLOCK                0xa3
+#define ALAUDA_BULK_WRITE_PAGE         0xa4
+#define ALAUDA_BULK_WRITE_BLOCK                0xb4
+#define ALAUDA_BULK_RESET_MEDIA                0xe0
+
+/* Address shifting */
+#define PBA_LO(pba) ((pba & 0xF) << 5)
+#define PBA_HI(pba) (pba >> 3)
+#define PBA_ZONE(pba) (pba >> 11)
+
+#define TIMEOUT HZ
+
+static struct usb_device_id alauda_table [] = {
+       { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */
+       { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */
+       { }
+};
+MODULE_DEVICE_TABLE(usb, alauda_table);
+
+struct alauda_card {
+       u8      id;             /* id byte */
+       u8      chipshift;      /* 1<<chipshift total size */
+       u8      pageshift;      /* 1<<pageshift page size */
+       u8      blockshift;     /* 1<<blockshift block size */
+};
+
+struct alauda {
+       struct usb_device       *dev;
+       struct usb_interface    *interface;
+       struct mtd_info         *mtd;
+       struct alauda_card      *card;
+       struct mutex            card_mutex;
+       u32                     pagemask;
+       u32                     bytemask;
+       u32                     blockmask;
+       unsigned int            write_out;
+       unsigned int            bulk_in;
+       unsigned int            bulk_out;
+       u8                      port;
+       struct kref             kref;
+};
+
+static struct alauda_card alauda_card_ids[] = {
+       /* NAND flash */
+       { 0x6e, 20, 8, 12},     /* 1 MB */
+       { 0xe8, 20, 8, 12},     /* 1 MB */
+       { 0xec, 20, 8, 12},     /* 1 MB */
+       { 0x64, 21, 8, 12},     /* 2 MB */
+       { 0xea, 21, 8, 12},     /* 2 MB */
+       { 0x6b, 22, 9, 13},     /* 4 MB */
+       { 0xe3, 22, 9, 13},     /* 4 MB */
+       { 0xe5, 22, 9, 13},     /* 4 MB */
+       { 0xe6, 23, 9, 13},     /* 8 MB */
+       { 0x73, 24, 9, 14},     /* 16 MB */
+       { 0x75, 25, 9, 14},     /* 32 MB */
+       { 0x76, 26, 9, 14},     /* 64 MB */
+       { 0x79, 27, 9, 14},     /* 128 MB */
+       { 0x71, 28, 9, 14},     /* 256 MB */
+
+       /* MASK ROM */
+       { 0x5d, 21, 9, 13},     /* 2 MB */
+       { 0xd5, 22, 9, 13},     /* 4 MB */
+       { 0xd6, 23, 9, 13},     /* 8 MB */
+       { 0x57, 24, 9, 13},     /* 16 MB */
+       { 0x58, 25, 9, 13},     /* 32 MB */
+       { }
+};
+
+static struct alauda_card *get_card(u8 id)
+{
+       struct alauda_card *card;
+
+       for (card = alauda_card_ids; card->id; card++)
+               if (card->id == id)
+                       return card;
+       return NULL;
+}
+
+static void alauda_delete(struct kref *kref)
+{
+       struct alauda *al = container_of(kref, struct alauda, kref);
+
+       if (al->mtd) {
+               del_mtd_device(al->mtd);
+               kfree(al->mtd);
+       }
+       usb_put_dev(al->dev);
+       kfree(al);
+}
+
+static int alauda_get_media_status(struct alauda *al, void *buf)
+{
+       int ret;
+
+       mutex_lock(&al->card_mutex);
+       ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0),
+                       ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ);
+       mutex_unlock(&al->card_mutex);
+       return ret;
+}
+
+static int alauda_ack_media(struct alauda *al)
+{
+       int ret;
+
+       mutex_lock(&al->card_mutex);
+       ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0),
+                       ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ);
+       mutex_unlock(&al->card_mutex);
+       return ret;
+}
+
+static int alauda_get_media_signatures(struct alauda *al, void *buf)
+{
+       int ret;
+
+       mutex_lock(&al->card_mutex);
+       ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0),
+                       ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ);
+       mutex_unlock(&al->card_mutex);
+       return ret;
+}
+
+static void alauda_reset(struct alauda *al)
+{
+       u8 command[] = {
+               ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0,
+               0, 0, 0, 0, al->port
+       };
+       mutex_lock(&al->card_mutex);
+       usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ);
+       mutex_unlock(&al->card_mutex);
+}
+
+static void correct_data(void *buf, void *read_ecc,
+               int *corrected, int *uncorrected)
+{
+       u8 calc_ecc[3];
+       int err;
+
+       nand_calculate_ecc(NULL, buf, calc_ecc);
+       err = nand_correct_data(NULL, buf, read_ecc, calc_ecc);
+       if (err) {
+               if (err > 0)
+                       (*corrected)++;
+               else
+                       (*uncorrected)++;
+       }
+}
+
+struct alauda_sg_request {
+       struct urb *urb[3];
+       struct completion comp;
+};
+
+static void alauda_complete(struct urb *urb)
+{
+       struct completion *comp = urb->context;
+
+       if (comp)
+               complete(comp);
+}
+
+static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf,
+               void *oob)
+{
+       struct alauda_sg_request sg;
+       struct alauda *al = mtd->priv;
+       u32 pba = from >> al->card->blockshift;
+       u32 page = (from >> al->card->pageshift) & al->pagemask;
+       u8 command[] = {
+               ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba),
+               PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port
+       };
+       int i, err;
+
+       for (i=0; i<3; i++)
+               sg.urb[i] = NULL;
+
+       err = -ENOMEM;
+       for (i=0; i<3; i++) {
+               sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
+               if (!sg.urb[i])
+                       goto out;
+       }
+       init_completion(&sg.comp);
+       usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
+                       alauda_complete, NULL);
+       usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize,
+                       alauda_complete, NULL);
+       usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16,
+                       alauda_complete, &sg.comp);
+
+       mutex_lock(&al->card_mutex);
+       for (i=0; i<3; i++) {
+               err = usb_submit_urb(sg.urb[i], GFP_NOIO);
+               if (err)
+                       goto cancel;
+       }
+       if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
+               err = -ETIMEDOUT;
+cancel:
+               for (i=0; i<3; i++) {
+                       usb_kill_urb(sg.urb[i]);
+               }
+       }
+       mutex_unlock(&al->card_mutex);
+
+out:
+       usb_free_urb(sg.urb[0]);
+       usb_free_urb(sg.urb[1]);
+       usb_free_urb(sg.urb[2]);
+       return err;
+}
+
+static int alauda_read_page(struct mtd_info *mtd, loff_t from,
+               void *buf, u8 *oob, int *corrected, int *uncorrected)
+{
+       int err;
+
+       err = __alauda_read_page(mtd, from, buf, oob);
+       if (err)
+               return err;
+       correct_data(buf, oob+13, corrected, uncorrected);
+       correct_data(buf+256, oob+8, corrected, uncorrected);
+       return 0;
+}
+
+static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf,
+               void *oob)
+{
+       struct alauda_sg_request sg;
+       struct alauda *al = mtd->priv;
+       u32 pba = to >> al->card->blockshift;
+       u32 page = (to >> al->card->pageshift) & al->pagemask;
+       u8 command[] = {
+               ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba),
+               PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port
+       };
+       int i, err;
+
+       for (i=0; i<3; i++)
+               sg.urb[i] = NULL;
+
+       err = -ENOMEM;
+       for (i=0; i<3; i++) {
+               sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
+               if (!sg.urb[i])
+                       goto out;
+       }
+       init_completion(&sg.comp);
+       usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
+                       alauda_complete, NULL);
+       usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize,
+                       alauda_complete, NULL);
+       usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16,
+                       alauda_complete, &sg.comp);
+
+       mutex_lock(&al->card_mutex);
+       for (i=0; i<3; i++) {
+               err = usb_submit_urb(sg.urb[i], GFP_NOIO);
+               if (err)
+                       goto cancel;
+       }
+       if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
+               err = -ETIMEDOUT;
+cancel:
+               for (i=0; i<3; i++) {
+                       usb_kill_urb(sg.urb[i]);
+               }
+       }
+       mutex_unlock(&al->card_mutex);
+
+out:
+       usb_free_urb(sg.urb[0]);
+       usb_free_urb(sg.urb[1]);
+       usb_free_urb(sg.urb[2]);
+       return err;
+}
+
+static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs)
+{
+       struct alauda_sg_request sg;
+       struct alauda *al = mtd->priv;
+       u32 pba = ofs >> al->card->blockshift;
+       u8 command[] = {
+               ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba),
+               PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port
+       };
+       u8 buf[2];
+       int i, err;
+
+       for (i=0; i<2; i++)
+               sg.urb[i] = NULL;
+
+       err = -ENOMEM;
+       for (i=0; i<2; i++) {
+               sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
+               if (!sg.urb[i])
+                       goto out;
+       }
+       init_completion(&sg.comp);
+       usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
+                       alauda_complete, NULL);
+       usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2,
+                       alauda_complete, &sg.comp);
+
+       mutex_lock(&al->card_mutex);
+       for (i=0; i<2; i++) {
+               err = usb_submit_urb(sg.urb[i], GFP_NOIO);
+               if (err)
+                       goto cancel;
+       }
+       if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
+               err = -ETIMEDOUT;
+cancel:
+               for (i=0; i<2; i++) {
+                       usb_kill_urb(sg.urb[i]);
+               }
+       }
+       mutex_unlock(&al->card_mutex);
+
+out:
+       usb_free_urb(sg.urb[0]);
+       usb_free_urb(sg.urb[1]);
+       return err;
+}
+
+static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob)
+{
+       static u8 ignore_buf[512]; /* write only */
+
+       return __alauda_read_page(mtd, from, ignore_buf, oob);
+}
+
+static int popcount8(u8 c)
+{
+       int ret = 0;
+
+       for ( ; c; c>>=1)
+               ret += c & 1;
+       return ret;
+}
+
+static int alauda_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+       u8 oob[16];
+       int err;
+
+       err = alauda_read_oob(mtd, ofs, oob);
+       if (err)
+               return err;
+
+       /* A block is marked bad if two or more bits are zero */
+       return popcount8(oob[5]) >= 7 ? 0 : 1;
+}
+
+static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char *buf)
+{
+       struct alauda *al = mtd->priv;
+       void *bounce_buf;
+       int err, corrected=0, uncorrected=0;
+
+       bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL);
+       if (!bounce_buf)
+               return -ENOMEM;
+
+       *retlen = len;
+       while (len) {
+               u8 oob[16];
+               size_t byte = from & al->bytemask;
+               size_t cplen = min(len, mtd->writesize - byte);
+
+               err = alauda_read_page(mtd, from, bounce_buf, oob,
+                               &corrected, &uncorrected);
+               if (err)
+                       goto out;
+
+               memcpy(buf, bounce_buf + byte, cplen);
+               buf += cplen;
+               from += cplen;
+               len -= cplen;
+       }
+       err = 0;
+       if (corrected)
+               err = -EUCLEAN;
+       if (uncorrected)
+               err = -EBADMSG;
+out:
+       kfree(bounce_buf);
+       return err;
+}
+
+static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char *buf)
+{
+       struct alauda *al = mtd->priv;
+       int err, corrected=0, uncorrected=0;
+
+       if ((from & al->bytemask) || (len & al->bytemask))
+               return alauda_bounce_read(mtd, from, len, retlen, buf);
+
+       *retlen = len;
+       while (len) {
+               u8 oob[16];
+
+               err = alauda_read_page(mtd, from, buf, oob,
+                               &corrected, &uncorrected);
+               if (err)
+                       return err;
+
+               buf += mtd->writesize;
+               from += mtd->writesize;
+               len -= mtd->writesize;
+       }
+       err = 0;
+       if (corrected)
+               err = -EUCLEAN;
+       if (uncorrected)
+               err = -EBADMSG;
+       return err;
+}
+
+static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len,
+               size_t *retlen, const u_char *buf)
+{
+       struct alauda *al = mtd->priv;
+       int err;
+
+       if ((to & al->bytemask) || (len & al->bytemask))
+               return -EINVAL;
+
+       *retlen = len;
+       while (len) {
+               u32 page = (to >> al->card->pageshift) & al->pagemask;
+               u8 oob[16] = {  'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff,
+                               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+               /* don't write to bad blocks */
+               if (page == 0) {
+                       err = alauda_isbad(mtd, to);
+                       if (err) {
+                               return -EIO;
+                       }
+               }
+               nand_calculate_ecc(mtd, buf, &oob[13]);
+               nand_calculate_ecc(mtd, buf+256, &oob[8]);
+
+               err = alauda_write_page(mtd, to, (void*)buf, oob);
+               if (err)
+                       return err;
+
+               buf += mtd->writesize;
+               to += mtd->writesize;
+               len -= mtd->writesize;
+       }
+       return 0;
+}
+
+static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       struct alauda *al = mtd->priv;
+       u32 ofs = instr->addr;
+       u32 len = instr->len;
+       int err;
+
+       if ((ofs & al->blockmask) || (len & al->blockmask))
+               return -EINVAL;
+
+       while (len) {
+               /* don't erase bad blocks */
+               err = alauda_isbad(mtd, ofs);
+               if (err > 0)
+                       err = -EIO;
+               if (err < 0)
+                       return err;
+
+               err = alauda_erase_block(mtd, ofs);
+               if (err < 0)
+                       return err;
+
+               ofs += mtd->erasesize;
+               len -= mtd->erasesize;
+       }
+       return 0;
+}
+
+static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       int err;
+
+       err = __alauda_erase(mtd, instr);
+       instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
+       mtd_erase_callback(instr);
+       return err;
+}
+
+static int alauda_init_media(struct alauda *al)
+{
+       u8 buf[4], *b0=buf, *b1=buf+1;
+       struct alauda_card *card;
+       struct mtd_info *mtd;
+       int err;
+
+       mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
+       if (!mtd)
+               return -ENOMEM;
+
+       for (;;) {
+               err = alauda_get_media_status(al, buf);
+               if (err < 0)
+                       goto error;
+               if (*b0 & 0x10)
+                       break;
+               msleep(20);
+       }
+
+       err = alauda_ack_media(al);
+       if (err)
+               goto error;
+
+       msleep(10);
+
+       err = alauda_get_media_status(al, buf);
+       if (err < 0)
+               goto error;
+
+       if (*b0 != 0x14) {
+               /* media not ready */
+               err = -EIO;
+               goto error;
+       }
+       err = alauda_get_media_signatures(al, buf);
+       if (err < 0)
+               goto error;
+
+       card = get_card(*b1);
+       if (!card) {
+               printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1);
+               err = -EIO;
+               goto error;
+       }
+       printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n",
+                       1<<card->pageshift, 1<<card->blockshift,
+                       1<<(card->chipshift-20));
+       al->card = card;
+       al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1;
+       al->bytemask = (1 << card->pageshift) - 1;
+       al->blockmask = (1 << card->blockshift) - 1;
+
+       mtd->name = "alauda";
+       mtd->size = 1<<card->chipshift;
+       mtd->erasesize = 1<<card->blockshift;
+       mtd->writesize = 1<<card->pageshift;
+       mtd->type = MTD_NANDFLASH;
+       mtd->flags = MTD_CAP_NANDFLASH;
+       mtd->read = alauda_read;
+       mtd->write = alauda_write;
+       mtd->erase = alauda_erase;
+       mtd->block_isbad = alauda_isbad;
+       mtd->priv = al;
+       mtd->owner = THIS_MODULE;
+
+       err = add_mtd_device(mtd);
+       if (err) {
+               err = -ENFILE;
+               goto error;
+       }
+
+       al->mtd = mtd;
+       alauda_reset(al); /* no clue whether this is necessary */
+       return 0;
+error:
+       kfree(mtd);
+       return err;
+}
+
+static int alauda_check_media(struct alauda *al)
+{
+       u8 buf[2], *b0 = buf, *b1 = buf+1;
+       int err;
+
+       err = alauda_get_media_status(al, buf);
+       if (err < 0)
+               return err;
+
+       if ((*b1 & 0x01) == 0) {
+               /* door open */
+               return -EIO;
+       }
+       if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) {
+               /* no media ? */
+               return -EIO;
+       }
+       if (*b0 & 0x08) {
+               /* media change ? */
+               return alauda_init_media(al);
+       }
+       return 0;
+}
+
+static int alauda_probe(struct usb_interface *interface,
+               const struct usb_device_id *id)
+{
+       struct alauda *al;
+       struct usb_host_interface *iface;
+       struct usb_endpoint_descriptor *ep,
+                       *ep_in=NULL, *ep_out=NULL, *ep_wr=NULL;
+       int i, err = -ENOMEM;
+
+       al = kzalloc(2*sizeof(*al), GFP_KERNEL);
+       if (!al)
+               goto error;
+
+       kref_init(&al->kref);
+       usb_set_intfdata(interface, al);
+
+       al->dev = usb_get_dev(interface_to_usbdev(interface));
+       al->interface = interface;
+
+       iface = interface->cur_altsetting;
+       for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+               ep = &iface->endpoint[i].desc;
+
+               if (usb_endpoint_is_bulk_in(ep)) {
+                       ep_in = ep;
+               } else if (usb_endpoint_is_bulk_out(ep)) {
+                       if (i==0)
+                               ep_wr = ep;
+                       else
+                               ep_out = ep;
+               }
+       }
+       err = -EIO;
+       if (!ep_wr || !ep_in || !ep_out)
+               goto error;
+
+       al->write_out = usb_sndbulkpipe(al->dev,
+                       ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+       al->bulk_in = usb_rcvbulkpipe(al->dev,
+                       ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+       al->bulk_out = usb_sndbulkpipe(al->dev,
+                       ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+       /* second device is identical up to now */
+       memcpy(al+1, al, sizeof(*al));
+
+       mutex_init(&al[0].card_mutex);
+       mutex_init(&al[1].card_mutex);
+
+       al[0].port = ALAUDA_PORT_XD;
+       al[1].port = ALAUDA_PORT_SM;
+
+       info("alauda probed");
+       alauda_check_media(al);
+       alauda_check_media(al+1);
+
+       return 0;
+
+error:
+       if (al)
+               kref_put(&al->kref, alauda_delete);
+       return err;
+}
+
+static void alauda_disconnect(struct usb_interface *interface)
+{
+       struct alauda *al;
+
+       al = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       /* FIXME: prevent more I/O from starting */
+
+       /* decrement our usage count */
+       if (al)
+               kref_put(&al->kref, alauda_delete);
+
+       info("alauda gone");
+}
+
+static struct usb_driver alauda_driver = {
+       .name =         "alauda",
+       .probe =        alauda_probe,
+       .disconnect =   alauda_disconnect,
+       .id_table =     alauda_table,
+};
+
+static int __init alauda_init(void)
+{
+       return usb_register(&alauda_driver);
+}
+
+static void __exit alauda_exit(void)
+{
+       usb_deregister(&alauda_driver);
+}
+
+module_init(alauda_init);
+module_exit(alauda_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
new file mode 100644 (file)
index 0000000..1657ecd
--- /dev/null
@@ -0,0 +1,788 @@
+/* linux/drivers/mtd/nand/bf5xx_nand.c
+ *
+ * Copyright 2006-2007 Analog Devices Inc.
+ *     http://blackfin.uclinux.org/
+ *     Bryan Wu <bryan.wu@analog.com>
+ *
+ * Blackfin BF5xx on-chip NAND flash controler driver
+ *
+ * Derived from drivers/mtd/nand/s3c2410.c
+ * Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk>
+ *
+ * Derived from drivers/mtd/nand/cafe.c
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Changelog:
+ *     12-Jun-2007  Bryan Wu:  Initial version
+ *     18-Jul-2007  Bryan Wu:
+ *             - ECC_HW and ECC_SW supported
+ *             - DMA supported in ECC_HW
+ *             - YAFFS tested as rootfs in both ECC_HW and ECC_SW
+ *
+ * TODO:
+ *     Enable JFFS2 over NAND as rootfs
+ *
+ * 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/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/blackfin.h>
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+#include <asm/nand.h>
+#include <asm/portmux.h>
+
+#define DRV_NAME       "bf5xx-nand"
+#define DRV_VERSION    "1.2"
+#define DRV_AUTHOR     "Bryan Wu <bryan.wu@analog.com>"
+#define DRV_DESC       "BF5xx on-chip NAND FLash Controller Driver"
+
+#ifdef CONFIG_MTD_NAND_BF5XX_HWECC
+static int hardware_ecc = 1;
+#else
+static int hardware_ecc;
+#endif
+
+static unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, 0};
+
+/*
+ * Data structures for bf5xx nand flash controller driver
+ */
+
+/* bf5xx nand info */
+struct bf5xx_nand_info {
+       /* mtd info */
+       struct nand_hw_control          controller;
+       struct mtd_info                 mtd;
+       struct nand_chip                chip;
+
+       /* platform info */
+       struct bf5xx_nand_platform      *platform;
+
+       /* device info */
+       struct device                   *device;
+
+       /* DMA stuff */
+       struct completion               dma_completion;
+};
+
+/*
+ * Conversion functions
+ */
+static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd)
+{
+       return container_of(mtd, struct bf5xx_nand_info, mtd);
+}
+
+static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
+{
+       return platform_get_drvdata(pdev);
+}
+
+static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
+{
+       return pdev->dev.platform_data;
+}
+
+/*
+ * struct nand_chip interface function pointers
+ */
+
+/*
+ * bf5xx_nand_hwcontrol
+ *
+ * Issue command and address cycles to the chip
+ */
+static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+                                  unsigned int ctrl)
+{
+       if (cmd == NAND_CMD_NONE)
+               return;
+
+       while (bfin_read_NFC_STAT() & WB_FULL)
+               cpu_relax();
+
+       if (ctrl & NAND_CLE)
+               bfin_write_NFC_CMD(cmd);
+       else
+               bfin_write_NFC_ADDR(cmd);
+       SSYNC();
+}
+
+/*
+ * bf5xx_nand_devready()
+ *
+ * returns 0 if the nand is busy, 1 if it is ready
+ */
+static int bf5xx_nand_devready(struct mtd_info *mtd)
+{
+       unsigned short val = bfin_read_NFC_IRQSTAT();
+
+       if ((val & NBUSYIRQ) == NBUSYIRQ)
+               return 1;
+       else
+               return 0;
+}
+
+/*
+ * ECC functions
+ * These allow the bf5xx to use the controller's ECC
+ * generator block to ECC the data as it passes through
+ */
+
+/*
+ * ECC error correction function
+ */
+static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
+                                       u_char *read_ecc, u_char *calc_ecc)
+{
+       struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+       u32 syndrome[5];
+       u32 calced, stored;
+       int i;
+       unsigned short failing_bit, failing_byte;
+       u_char data;
+
+       calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
+       stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
+
+       syndrome[0] = (calced ^ stored);
+
+       /*
+        * syndrome 0: all zero
+        * No error in data
+        * No action
+        */
+       if (!syndrome[0] || !calced || !stored)
+               return 0;
+
+       /*
+        * sysdrome 0: only one bit is one
+        * ECC data was incorrect
+        * No action
+        */
+       if (hweight32(syndrome[0]) == 1) {
+               dev_err(info->device, "ECC data was incorrect!\n");
+               return 1;
+       }
+
+       syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
+       syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
+       syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
+       syndrome[4] = syndrome[2] ^ syndrome[3];
+
+       for (i = 0; i < 5; i++)
+               dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]);
+
+       dev_info(info->device,
+               "calced[0x%08x], stored[0x%08x]\n",
+               calced, stored);
+
+       /*
+        * sysdrome 0: exactly 11 bits are one, each parity
+        * and parity' pair is 1 & 0 or 0 & 1.
+        * 1-bit correctable error
+        * Correct the error
+        */
+       if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
+               dev_info(info->device,
+                       "1-bit correctable error, correct it.\n");
+               dev_info(info->device,
+                       "syndrome[1] 0x%08x\n", syndrome[1]);
+
+               failing_bit = syndrome[1] & 0x7;
+               failing_byte = syndrome[1] >> 0x3;
+               data = *(dat + failing_byte);
+               data = data ^ (0x1 << failing_bit);
+               *(dat + failing_byte) = data;
+
+               return 0;
+       }
+
+       /*
+        * sysdrome 0: random data
+        * More than 1-bit error, non-correctable error
+        * Discard data, mark bad block
+        */
+       dev_err(info->device,
+               "More than 1-bit error, non-correctable error.\n");
+       dev_err(info->device,
+               "Please discard data, mark bad block\n");
+
+       return 1;
+}
+
+static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+                                       u_char *read_ecc, u_char *calc_ecc)
+{
+       struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+       struct bf5xx_nand_platform *plat = info->platform;
+       unsigned short page_size = (plat->page_size ? 512 : 256);
+       int ret;
+
+       ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
+
+       /* If page size is 512, correct second 256 bytes */
+       if (page_size == 512) {
+               dat += 256;
+               read_ecc += 8;
+               calc_ecc += 8;
+               ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
+       }
+
+       return ret;
+}
+
+static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       return;
+}
+
+static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
+               const u_char *dat, u_char *ecc_code)
+{
+       struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+       struct bf5xx_nand_platform *plat = info->platform;
+       u16 page_size = (plat->page_size ? 512 : 256);
+       u16 ecc0, ecc1;
+       u32 code[2];
+       u8 *p;
+       int bytes = 3, i;
+
+       /* first 4 bytes ECC code for 256 page size */
+       ecc0 = bfin_read_NFC_ECC0();
+       ecc1 = bfin_read_NFC_ECC1();
+
+       code[0] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11);
+
+       dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);
+
+       /* second 4 bytes ECC code for 512 page size */
+       if (page_size == 512) {
+               ecc0 = bfin_read_NFC_ECC2();
+               ecc1 = bfin_read_NFC_ECC3();
+               code[1] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11);
+               bytes = 6;
+               dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]);
+       }
+
+       p = (u8 *)code;
+       for (i = 0; i < bytes; i++)
+               ecc_code[i] = p[i];
+
+       return 0;
+}
+
+/*
+ * PIO mode for buffer writing and reading
+ */
+static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       int i;
+       unsigned short val;
+
+       /*
+        * Data reads are requested by first writing to NFC_DATA_RD
+        * and then reading back from NFC_READ.
+        */
+       for (i = 0; i < len; i++) {
+               while (bfin_read_NFC_STAT() & WB_FULL)
+                       cpu_relax();
+
+               /* Contents do not matter */
+               bfin_write_NFC_DATA_RD(0x0000);
+               SSYNC();
+
+               while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY)
+                       cpu_relax();
+
+               buf[i] = bfin_read_NFC_READ();
+
+               val = bfin_read_NFC_IRQSTAT();
+               val |= RD_RDY;
+               bfin_write_NFC_IRQSTAT(val);
+               SSYNC();
+       }
+}
+
+static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd)
+{
+       uint8_t val;
+
+       bf5xx_nand_read_buf(mtd, &val, 1);
+
+       return val;
+}
+
+static void bf5xx_nand_write_buf(struct mtd_info *mtd,
+                               const uint8_t *buf, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               while (bfin_read_NFC_STAT() & WB_FULL)
+                       cpu_relax();
+
+               bfin_write_NFC_DATA_WR(buf[i]);
+               SSYNC();
+       }
+}
+
+static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       int i;
+       u16 *p = (u16 *) buf;
+       len >>= 1;
+
+       /*
+        * Data reads are requested by first writing to NFC_DATA_RD
+        * and then reading back from NFC_READ.
+        */
+       bfin_write_NFC_DATA_RD(0x5555);
+
+       SSYNC();
+
+       for (i = 0; i < len; i++)
+               p[i] = bfin_read_NFC_READ();
+}
+
+static void bf5xx_nand_write_buf16(struct mtd_info *mtd,
+                               const uint8_t *buf, int len)
+{
+       int i;
+       u16 *p = (u16 *) buf;
+       len >>= 1;
+
+       for (i = 0; i < len; i++)
+               bfin_write_NFC_DATA_WR(p[i]);
+
+       SSYNC();
+}
+
+/*
+ * DMA functions for buffer writing and reading
+ */
+static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
+{
+       struct bf5xx_nand_info *info = dev_id;
+
+       clear_dma_irqstat(CH_NFC);
+       disable_dma(CH_NFC);
+       complete(&info->dma_completion);
+
+       return IRQ_HANDLED;
+}
+
+static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
+                               uint8_t *buf, int is_read)
+{
+       struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+       struct bf5xx_nand_platform *plat = info->platform;
+       unsigned short page_size = (plat->page_size ? 512 : 256);
+       unsigned short val;
+
+       dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n",
+                       mtd, buf, is_read);
+
+       /*
+        * Before starting a dma transfer, be sure to invalidate/flush
+        * the cache over the address range of your DMA buffer to
+        * prevent cache coherency problems. Otherwise very subtle bugs
+        * can be introduced to your driver.
+        */
+       if (is_read)
+               invalidate_dcache_range((unsigned int)buf,
+                               (unsigned int)(buf + page_size));
+       else
+               flush_dcache_range((unsigned int)buf,
+                               (unsigned int)(buf + page_size));
+
+       /*
+        * This register must be written before each page is
+        * transferred to generate the correct ECC register
+        * values.
+        */
+       bfin_write_NFC_RST(0x1);
+       SSYNC();
+
+       disable_dma(CH_NFC);
+       clear_dma_irqstat(CH_NFC);
+
+       /* setup DMA register with Blackfin DMA API */
+       set_dma_config(CH_NFC, 0x0);
+       set_dma_start_addr(CH_NFC, (unsigned long) buf);
+       set_dma_x_count(CH_NFC, (page_size >> 2));
+       set_dma_x_modify(CH_NFC, 4);
+
+       /* setup write or read operation */
+       val = DI_EN | WDSIZE_32;
+       if (is_read)
+               val |= WNR;
+       set_dma_config(CH_NFC, val);
+       enable_dma(CH_NFC);
+
+       /* Start PAGE read/write operation */
+       if (is_read)
+               bfin_write_NFC_PGCTL(0x1);
+       else
+               bfin_write_NFC_PGCTL(0x2);
+       wait_for_completion(&info->dma_completion);
+
+       return 0;
+}
+
+static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
+                                       uint8_t *buf, int len)
+{
+       struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+       struct bf5xx_nand_platform *plat = info->platform;
+       unsigned short page_size = (plat->page_size ? 512 : 256);
+
+       dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len);
+
+       if (len == page_size)
+               bf5xx_nand_dma_rw(mtd, buf, 1);
+       else
+               bf5xx_nand_read_buf(mtd, buf, len);
+}
+
+static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
+                               const uint8_t *buf, int len)
+{
+       struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
+       struct bf5xx_nand_platform *plat = info->platform;
+       unsigned short page_size = (plat->page_size ? 512 : 256);
+
+       dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len);
+
+       if (len == page_size)
+               bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0);
+       else
+               bf5xx_nand_write_buf(mtd, buf, len);
+}
+
+/*
+ * System initialization functions
+ */
+
+static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
+{
+       int ret;
+       unsigned short val;
+
+       /* Do not use dma */
+       if (!hardware_ecc)
+               return 0;
+
+       init_completion(&info->dma_completion);
+
+       /* Setup DMAC1 channel mux for NFC which shared with SDH */
+       val = bfin_read_DMAC1_PERIMUX();
+       val &= 0xFFFE;
+       bfin_write_DMAC1_PERIMUX(val);
+       SSYNC();
+
+       /* Request NFC DMA channel */
+       ret = request_dma(CH_NFC, "BF5XX NFC driver");
+       if (ret < 0) {
+               dev_err(info->device, " unable to get DMA channel\n");
+               return ret;
+       }
+
+       set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info);
+
+       /* Turn off the DMA channel first */
+       disable_dma(CH_NFC);
+       return 0;
+}
+
+/*
+ * BF5XX NFC hardware initialization
+ *  - pin mux setup
+ *  - clear interrupt status
+ */
+static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
+{
+       int err = 0;
+       unsigned short val;
+       struct bf5xx_nand_platform *plat = info->platform;
+
+       /* setup NFC_CTL register */
+       dev_info(info->device,
+               "page_size=%d, data_width=%d, wr_dly=%d, rd_dly=%d\n",
+               (plat->page_size ? 512 : 256),
+               (plat->data_width ? 16 : 8),
+               plat->wr_dly, plat->rd_dly);
+
+       val = (plat->page_size << NFC_PG_SIZE_OFFSET) |
+               (plat->data_width << NFC_NWIDTH_OFFSET) |
+               (plat->rd_dly << NFC_RDDLY_OFFSET) |
+               (plat->rd_dly << NFC_WRDLY_OFFSET);
+       dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val);
+
+       bfin_write_NFC_CTL(val);
+       SSYNC();
+
+       /* clear interrupt status */
+       bfin_write_NFC_IRQMASK(0x0);
+       SSYNC();
+       val = bfin_read_NFC_IRQSTAT();
+       bfin_write_NFC_IRQSTAT(val);
+       SSYNC();
+
+       if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) {
+               printk(KERN_ERR DRV_NAME
+               ": Requesting Peripherals failed\n");
+               return -EFAULT;
+       }
+
+       /* DMA initialization  */
+       if (bf5xx_nand_dma_init(info))
+               err = -ENXIO;
+
+       return err;
+}
+
+/*
+ * Device management interface
+ */
+static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
+{
+       struct mtd_info *mtd = &info->mtd;
+
+#ifdef CONFIG_MTD_PARTITIONS
+       struct mtd_partition *parts = info->platform->partitions;
+       int nr = info->platform->nr_partitions;
+
+       return add_mtd_partitions(mtd, parts, nr);
+#else
+       return add_mtd_device(mtd);
+#endif
+}
+
+static int bf5xx_nand_remove(struct platform_device *pdev)
+{
+       struct bf5xx_nand_info *info = to_nand_info(pdev);
+       struct mtd_info *mtd = NULL;
+
+       platform_set_drvdata(pdev, NULL);
+
+       /* first thing we need to do is release all our mtds
+        * and their partitions, then go through freeing the
+        * resources used
+        */
+       mtd = &info->mtd;
+       if (mtd) {
+               nand_release(mtd);
+               kfree(mtd);
+       }
+
+       peripheral_free_list(bfin_nfc_pin_req);
+
+       /* free the common resources */
+       kfree(info);
+
+       return 0;
+}
+
+/*
+ * bf5xx_nand_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code checks to see if
+ * it can allocate all necessary resources then calls the
+ * nand layer to look for devices
+ */
+static int bf5xx_nand_probe(struct platform_device *pdev)
+{
+       struct bf5xx_nand_platform *plat = to_nand_plat(pdev);
+       struct bf5xx_nand_info *info = NULL;
+       struct nand_chip *chip = NULL;
+       struct mtd_info *mtd = NULL;
+       int err = 0;
+
+       dev_dbg(&pdev->dev, "(%p)\n", pdev);
+
+       if (!plat) {
+               dev_err(&pdev->dev, "no platform specific information\n");
+               goto exit_error;
+       }
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (info == NULL) {
+               dev_err(&pdev->dev, "no memory for flash info\n");
+               err = -ENOMEM;
+               goto exit_error;
+       }
+
+       platform_set_drvdata(pdev, info);
+
+       spin_lock_init(&info->controller.lock);
+       init_waitqueue_head(&info->controller.wq);
+
+       info->device     = &pdev->dev;
+       info->platform   = plat;
+
+       /* initialise chip data struct */
+       chip = &info->chip;
+
+       if (plat->data_width)
+               chip->options |= NAND_BUSWIDTH_16;
+
+       chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN;
+
+       chip->read_buf = (plat->data_width) ?
+               bf5xx_nand_read_buf16 : bf5xx_nand_read_buf;
+       chip->write_buf = (plat->data_width) ?
+               bf5xx_nand_write_buf16 : bf5xx_nand_write_buf;
+
+       chip->read_byte    = bf5xx_nand_read_byte;
+
+       chip->cmd_ctrl     = bf5xx_nand_hwcontrol;
+       chip->dev_ready    = bf5xx_nand_devready;
+
+       chip->priv         = &info->mtd;
+       chip->controller   = &info->controller;
+
+       chip->IO_ADDR_R    = (void __iomem *) NFC_READ;
+       chip->IO_ADDR_W    = (void __iomem *) NFC_DATA_WR;
+
+       chip->chip_delay   = 0;
+
+       /* initialise mtd info data struct */
+       mtd             = &info->mtd;
+       mtd->priv       = chip;
+       mtd->owner      = THIS_MODULE;
+
+       /* initialise the hardware */
+       err = bf5xx_nand_hw_init(info);
+       if (err != 0)
+               goto exit_error;
+
+       /* setup hardware ECC data struct */
+       if (hardware_ecc) {
+               if (plat->page_size == NFC_PG_SIZE_256) {
+                       chip->ecc.bytes = 3;
+                       chip->ecc.size = 256;
+               } else if (plat->page_size == NFC_PG_SIZE_512) {
+                       chip->ecc.bytes = 6;
+                       chip->ecc.size = 512;
+               }
+
+               chip->read_buf      = bf5xx_nand_dma_read_buf;
+               chip->write_buf     = bf5xx_nand_dma_write_buf;
+               chip->ecc.calculate = bf5xx_nand_calculate_ecc;
+               chip->ecc.correct   = bf5xx_nand_correct_data;
+               chip->ecc.mode      = NAND_ECC_HW;
+               chip->ecc.hwctl     = bf5xx_nand_enable_hwecc;
+       } else {
+               chip->ecc.mode      = NAND_ECC_SOFT;
+       }
+
+       /* scan hardware nand chip and setup mtd info data struct */
+       if (nand_scan(mtd, 1)) {
+               err = -ENXIO;
+               goto exit_error;
+       }
+
+       /* add NAND partition */
+       bf5xx_nand_add_partition(info);
+
+       dev_dbg(&pdev->dev, "initialised ok\n");
+       return 0;
+
+exit_error:
+       bf5xx_nand_remove(pdev);
+
+       if (err == 0)
+               err = -EINVAL;
+       return err;
+}
+
+/* PM Support */
+#ifdef CONFIG_PM
+
+static int bf5xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
+{
+       struct bf5xx_nand_info *info = platform_get_drvdata(dev);
+
+       return 0;
+}
+
+static int bf5xx_nand_resume(struct platform_device *dev)
+{
+       struct bf5xx_nand_info *info = platform_get_drvdata(dev);
+
+       if (info)
+               bf5xx_nand_hw_init(info);
+
+       return 0;
+}
+
+#else
+#define bf5xx_nand_suspend NULL
+#define bf5xx_nand_resume NULL
+#endif
+
+/* driver device registration */
+static struct platform_driver bf5xx_nand_driver = {
+       .probe          = bf5xx_nand_probe,
+       .remove         = bf5xx_nand_remove,
+       .suspend        = bf5xx_nand_suspend,
+       .resume         = bf5xx_nand_resume,
+       .driver         = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init bf5xx_nand_init(void)
+{
+       printk(KERN_INFO "%s, Version %s (c) 2007 Analog Devices, Inc.\n",
+               DRV_DESC, DRV_VERSION);
+
+       return platform_driver_register(&bf5xx_nand_driver);
+}
+
+static void __exit bf5xx_nand_exit(void)
+{
+       platform_driver_unregister(&bf5xx_nand_driver);
+}
+
+module_init(bf5xx_nand_init);
+module_exit(bf5xx_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_DESCRIPTION(DRV_DESC);
index 6f32a35..e2832d0 100644 (file)
@@ -623,6 +623,11 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
        uint32_t ctrl;
        int err = 0;
 
+       /* Very old versions shared the same PCI ident for all three
+          functions on the chip. Verify the class too... */
+       if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH)
+               return -ENODEV;
+
        err = pci_enable_device(pdev);
        if (err)
                return err;
@@ -816,21 +821,57 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev)
 }
 
 static struct pci_device_id cafe_nand_tbl[] = {
-       { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 },
-       { 0, }
+       { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID },
+       { }
 };
 
 MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
 
+static int cafe_nand_resume(struct pci_dev *pdev)
+{
+       uint32_t ctrl;
+       struct mtd_info *mtd = pci_get_drvdata(pdev);
+       struct cafe_priv *cafe = mtd->priv;
+
+       /* 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);
+
+       /* Restore timing configuration */
+       cafe_writel(cafe, timing[0], NAND_TIMING1);
+       cafe_writel(cafe, timing[1], NAND_TIMING2);
+       cafe_writel(cafe, timing[2], NAND_TIMING3);
+
+        /* Disable master reset, enable NAND clock */
+       ctrl = cafe_readl(cafe, GLOBAL_CTRL);
+       ctrl &= 0xffffeff0;
+       ctrl |= 0x00007000;
+       cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
+       cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
+       cafe_writel(cafe, 0, NAND_DMA_CTRL);
+       cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
+       cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
+
+       /* Set up DMA address */
+       cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
+       if (sizeof(cafe->dmaaddr) > 4)
+       /* Shift in two parts to shut the compiler up */
+               cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1);
+       else
+               cafe_writel(cafe, 0, NAND_DMA_ADDR1);
+
+       /* Enable NAND IRQ in global IRQ mask register */
+       cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+       return 0;
+}
+
 static struct pci_driver cafe_nand_pci_driver = {
        .name = "CAFÉ NAND",
        .id_table = cafe_nand_tbl,
        .probe = cafe_nand_probe,
        .remove = __devexit_p(cafe_nand_remove),
-#ifdef CONFIG_PMx
-       .suspend = cafe_nand_suspend,
        .resume = cafe_nand_resume,
-#endif
 };
 
 static int cafe_nand_init(void)
index e96259f..ab9f5c5 100644 (file)
@@ -56,8 +56,6 @@ static unsigned long __initdata doc_locations[] = {
 #endif /*  CONFIG_MTD_DOCPROBE_HIGH */
 #elif defined(__PPC__)
        0xe4000000,
-#elif defined(CONFIG_MOMENCO_OCELOT_G)
-       0xff000000,
 #else
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #endif
index 7e9afc4..bed8729 100644 (file)
@@ -27,7 +27,6 @@
 #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>
index 24ac677..b4e0e77 100644 (file)
@@ -7,7 +7,7 @@
  *   Basic support for AG-AND chips is provided.
  *
  *     Additional technical information is available on
- *     http://www.linux-mtd.infradead.org/tech/nand.html
+ *     http://www.linux-mtd.infradead.org/doc/nand.html
  *
  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *               2002-2006 Thomas Gleixner (tglx@linutronix.de)
@@ -2069,13 +2069,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
  erase_exit:
 
        ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-       /* Do call back function */
-       if (!ret)
-               mtd_erase_callback(instr);
 
        /* Deselect and wake up anyone waiting on the device */
        nand_release_device(mtd);
 
+       /* Do call back function */
+       if (!ret)
+               mtd_erase_callback(instr);
+
        /*
         * If BBT requires refresh and erase was successful, rewrite any
         * selected bad block tables
index 2fc674a..a3e3ab0 100644 (file)
@@ -141,6 +141,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
        {NAND_MFR_STMICRO, "ST Micro"},
        {NAND_MFR_HYNIX, "Hynix"},
        {NAND_MFR_MICRON, "Micron"},
+       {NAND_MFR_AMD, "AMD"},
        {0x0, "Unknown"}
 };
 
index 205df0f..a757480 100644 (file)
@@ -1272,7 +1272,13 @@ static int prog_page(struct nandsim *ns, int num)
        mypage = NS_GET_PAGE(ns);
        if (mypage->byte == NULL) {
                NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
-               mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+               /*
+                * We allocate memory with GFP_NOFS because a flash FS may
+                * utilize this. If it is holding an FS lock, then gets here,
+                * then kmalloc runs writeback which goes to the FS again
+                * and deadlocks. This was seen in practice.
+                */
+               mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS);
                if (mypage->byte == NULL) {
                        NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
                        return -1;
index fd7a8d5..1c0e89f 100644 (file)
 #include <linux/platform_device.h>
 
 #include <asm/io.h>
+#ifdef CONFIG_40x
+#include <asm/ibm405.h>
+#else
 #include <asm/ibm44x.h>
+#endif
 
 struct ndfc_nand_mtd {
        struct mtd_info                 mtd;
@@ -230,7 +234,11 @@ static int ndfc_nand_probe(struct platform_device *pdev)
        struct ndfc_controller *ndfc = &ndfc_ctrl;
        unsigned long long phys = settings->ndfc_erpn | res->start;
 
+#ifndef CONFIG_PHYS_64BIT
+       ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1);
+#else
        ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
+#endif
        if (!ndfc->ndfcbase) {
                printk(KERN_ERR "NDFC: ioremap failed\n");
                return -EIO;
index 5fac4c4..b79a9cf 100644 (file)
@@ -60,8 +60,8 @@
 
 #include <asm/io.h>
 
-#include <asm/arch/regs-nand.h>
-#include <asm/arch/nand.h>
+#include <asm/plat-s3c/regs-nand.h>
+#include <asm/plat-s3c/nand.h>
 
 #ifdef CONFIG_MTD_NAND_S3C2410_HWECC
 static int hardware_ecc = 1;
index c257d39..cb41cbc 100644 (file)
@@ -40,4 +40,27 @@ config MTD_ONENAND_OTP
 
          OTP block is fully-guaranteed to be a valid block.
 
+config MTD_ONENAND_2X_PROGRAM
+       bool "OneNAND 2X program support"
+       help
+         The 2X Program is an extension of Program Operation.
+         Since the device is equipped with two DataRAMs, and two-plane NAND
+         Flash memory array, these two component enables simultaneous program
+         of 4KiB. Plane1 has only even blocks such as block0, block2, block4
+         while Plane2 has only odd blocks such as block1, block3, block5.
+         So MTD regards it as 4KiB page size and 256KiB block size
+
+         Now the following chips support it. (KFXXX16Q2M)
+           Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M,
+           Mux:   KFM2G16Q2M, KFN4G16Q2M,
+
+         And more recent chips
+
+config MTD_ONENAND_SIM
+       tristate "OneNAND simulator support"
+       depends on MTD_PARTITIONS
+       help
+         The simulator may simulate various OneNAND flash chips for the
+         OneNAND MTD layer.
+
 endif # MTD_ONENAND
index 269cfe4..4d2eacf 100644 (file)
@@ -8,4 +8,7 @@ obj-$(CONFIG_MTD_ONENAND)               += onenand.o
 # Board specific.
 obj-$(CONFIG_MTD_ONENAND_GENERIC)      += generic.o
 
+# Simulator
+obj-$(CONFIG_MTD_ONENAND_SIM)          += onenand_sim.o
+
 onenand-objs = onenand_base.o onenand_bbt.o
index 0537fac..b2c40f6 100644 (file)
@@ -206,6 +206,15 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
        default:
                block = (int) (addr >> this->erase_shift);
                page = (int) (addr >> this->page_shift);
+
+               if (ONENAND_IS_2PLANE(this)) {
+                       /* Make the even block number */
+                       block &= ~1;
+                       /* Is it the odd plane? */
+                       if (addr & this->writesize)
+                               block++;
+                       page >>= 1;
+               }
                page &= this->page_mask;
                break;
        }
@@ -216,8 +225,12 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                value = onenand_bufferram_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 
-               /* Switch to the next data buffer */
-               ONENAND_SET_NEXT_BUFFERRAM(this);
+               if (ONENAND_IS_2PLANE(this))
+                       /* It is always BufferRAM0 */
+                       ONENAND_SET_BUFFERRAM0(this);
+               else
+                       /* Switch to the next data buffer */
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
 
                return 0;
        }
@@ -247,6 +260,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                        break;
 
                default:
+                       if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+                               cmd = ONENAND_CMD_2X_PROG;
                        dataram = ONENAND_CURRENT_BUFFERRAM(this);
                        break;
                }
@@ -318,12 +333,14 @@ static int onenand_wait(struct mtd_info *mtd, int state)
        if (interrupt & ONENAND_INT_READ) {
                int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
                if (ecc) {
-                       printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
                        if (ecc & ONENAND_ECC_2BIT_ALL) {
+                               printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
                                mtd->ecc_stats.failed++;
                                return ecc;
-                       } else if (ecc & ONENAND_ECC_1BIT_ALL)
+                       } else if (ecc & ONENAND_ECC_1BIT_ALL) {
+                               printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc);
                                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);
@@ -445,8 +462,9 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
        struct onenand_chip *this = mtd->priv;
 
        if (ONENAND_CURRENT_BUFFERRAM(this)) {
+               /* Note: the 'this->writesize' is a real page size */
                if (area == ONENAND_DATARAM)
-                       return mtd->writesize;
+                       return this->writesize;
                if (area == ONENAND_SPARERAM)
                        return mtd->oobsize;
        }
@@ -572,6 +590,30 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
 }
 
 /**
+ * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
+ * @param mtd          MTD data structure
+ * @param addr         address to check
+ * @return             blockpage address
+ *
+ * Get blockpage address at 2x program mode
+ */
+static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
+{
+       struct onenand_chip *this = mtd->priv;
+       int blockpage, block, page;
+
+       /* Calculate the even block number */
+       block = (int) (addr >> this->erase_shift) & ~1;
+       /* Is it the odd plane? */
+       if (addr & this->writesize)
+               block++;
+       page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
+       blockpage = (block << 7) | page;
+
+       return blockpage;
+}
+
+/**
  * onenand_check_bufferram - [GENERIC] Check BufferRAM information
  * @param mtd          MTD data structure
  * @param addr         address to check
@@ -585,7 +627,10 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
        int blockpage, found = 0;
        unsigned int i;
 
-       blockpage = (int) (addr >> this->page_shift);
+       if (ONENAND_IS_2PLANE(this))
+               blockpage = onenand_get_2x_blockpage(mtd, addr);
+       else
+               blockpage = (int) (addr >> this->page_shift);
 
        /* Is there valid data? */
        i = ONENAND_CURRENT_BUFFERRAM(this);
@@ -625,7 +670,10 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
        int blockpage;
        unsigned int i;
 
-       blockpage = (int) (addr >> this->page_shift);
+       if (ONENAND_IS_2PLANE(this))
+               blockpage = onenand_get_2x_blockpage(mtd, addr);
+       else
+               blockpage = (int) (addr >> this->page_shift);
 
        /* Invalidate another BufferRAM */
        i = ONENAND_NEXT_BUFFERRAM(this);
@@ -717,36 +765,86 @@ static void onenand_release_device(struct mtd_info *mtd)
 }
 
 /**
- * onenand_read - [MTD Interface] Read data from flash
+ * 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;
+       unsigned int i;
+       uint8_t *oob_buf = this->oob_buf;
+
+       free = this->ecclayout->oobfree;
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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);
+       free = this->ecclayout->oobfree;
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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;
+               } else if (column == 0)
+                       break;
+       }
+       return 0;
+}
+
+/**
+ * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
  * @param mtd          MTD device structure
  * @param from         offset to read from
- * @param len          number of bytes to read
- * @param retlen       pointer to variable to store the number of read bytes
- * @param buf          the databuffer to put data
+ * @param ops:         oob operation description structure
  *
- * Read with ecc
-*/
-static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
-       size_t *retlen, u_char *buf)
+ * OneNAND read main and/or out-of-band data
+ */
+static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
+                               struct mtd_oob_ops *ops)
 {
        struct onenand_chip *this = mtd->priv;
        struct mtd_ecc_stats stats;
-       int read = 0, column;
-       int thislen;
+       size_t len = ops->len;
+       size_t ooblen = ops->ooblen;
+       u_char *buf = ops->datbuf;
+       u_char *oobbuf = ops->oobbuf;
+       int read = 0, column, thislen;
+       int oobread = 0, oobcolumn, thisooblen, oobsize;
        int ret = 0, boundary = 0;
+       int writesize = this->writesize;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+       if (ops->mode == MTD_OOB_AUTO)
+               oobsize = this->ecclayout->oobavail;
+       else
+               oobsize = mtd->oobsize;
+
+       oobcolumn = from & (mtd->oobsize - 1);
 
        /* Do not allow reads past end of device */
        if ((from + len) > mtd->size) {
-               printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n");
-               *retlen = 0;
+               printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n");
+               ops->retlen = 0;
+               ops->oobretlen = 0;
                return -EINVAL;
        }
 
-       /* Grab the lock and see if the device is available */
-       onenand_get_device(mtd, FL_READING);
-
        stats = mtd->ecc_stats;
 
        /* Read-while-load method */
@@ -754,22 +852,22 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
        /* Do first load to bufferRAM */
        if (read < len) {
                if (!onenand_check_bufferram(mtd, from)) {
-                       this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize);
+                       this->command(mtd, ONENAND_CMD_READ, from, writesize);
                        ret = this->wait(mtd, FL_READING);
                        onenand_update_bufferram(mtd, from, !ret);
                }
        }
 
-       thislen = min_t(int, mtd->writesize, len - read);
-       column = from & (mtd->writesize - 1);
-       if (column + thislen > mtd->writesize)
-               thislen = mtd->writesize - column;
+       thislen = min_t(int, writesize, len - read);
+       column = from & (writesize - 1);
+       if (column + thislen > writesize)
+               thislen = writesize - column;
 
        while (!ret) {
                /* If there is more to load then start next load */
                from += thislen;
                if (read + thislen < len) {
-                       this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize);
+                       this->command(mtd, ONENAND_CMD_READ, from, writesize);
                        /*
                         * Chip boundary handling in DDP
                         * Now we issued chip 1 read and pointed chip 1
@@ -785,6 +883,21 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
                }
                /* While load is going, read from last bufferRAM */
                this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
+
+               /* Read oob area if needed */
+               if (oobbuf) {
+                       thisooblen = oobsize - oobcolumn;
+                       thisooblen = min_t(int, thisooblen, ooblen - oobread);
+
+                       if (ops->mode == MTD_OOB_AUTO)
+                               onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
+                       else
+                               this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
+                       oobread += thisooblen;
+                       oobbuf += thisooblen;
+                       oobcolumn = 0;
+               }
+
                /* See if we are done */
                read += thislen;
                if (read == len)
@@ -794,7 +907,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
                        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);
+               thislen = min_t(int, writesize, len - read);
                column = 0;
                cond_resched();
                /* Now wait for load */
@@ -802,15 +915,13 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
                onenand_update_bufferram(mtd, from, !ret);
        }
 
-       /* Deselect and wake up anyone waiting on the device */
-       onenand_release_device(mtd);
-
        /*
         * Return success, if no ECC failures, else -EBADMSG
         * fs driver will take care of that, because
         * retlen == desired len and result == -EBADMSG
         */
-       *retlen = read;
+       ops->retlen = read;
+       ops->oobretlen = oobread;
 
        if (mtd->ecc_stats.failed - stats.failed)
                return -EBADMSG;
@@ -822,69 +933,29 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
 }
 
 /**
- * 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;
-       unsigned int i;
-       uint8_t *oob_buf = this->oob_buf;
-
-       free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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);
-       free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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;
-               } else if (column == 0)
-                       break;
-       }
-       return 0;
-}
-
-/**
- * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
+ * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
  * @param mtd          MTD device structure
  * @param from         offset to read from
- * @param len          number of bytes to read
- * @param retlen       pointer to variable to store the number of read bytes
- * @param buf          the databuffer to put data
- * @param mode         operation mode
+ * @param ops:         oob operation description structure
  *
  * OneNAND read out-of-band data from the spare area
  */
-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)
+static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
+                       struct mtd_oob_ops *ops)
 {
        struct onenand_chip *this = mtd->priv;
        int read = 0, thislen, column, oobsize;
+       size_t len = ops->ooblen;
+       mtd_oob_mode_t mode = ops->mode;
+       u_char *buf = ops->oobbuf;
        int ret = 0;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+       from += ops->ooboffs;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
 
        /* Initialize return length value */
-       *retlen = 0;
+       ops->oobretlen = 0;
 
        if (mode == MTD_OOB_AUTO)
                oobsize = this->ecclayout->oobavail;
@@ -894,7 +965,7 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
        column = from & (mtd->oobsize - 1);
 
        if (unlikely(column >= oobsize)) {
-               printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n");
+               printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n");
                return -EINVAL;
        }
 
@@ -902,13 +973,10 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
        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");
+               printk(KERN_ERR "onenand_read_oob_nolock: 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);
-
        while (read < len) {
                cond_resched();
 
@@ -928,7 +996,7 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
                        this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
 
                if (ret) {
-                       printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret);
+                       printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
                        break;
                }
 
@@ -947,22 +1015,52 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
                }
        }
 
-       /* Deselect and wake up anyone waiting on the device */
+       ops->oobretlen = read;
+       return ret;
+}
+
+/**
+ * onenand_read - [MTD Interface] Read data from flash
+ * @param mtd          MTD device structure
+ * @param from         offset to read from
+ * @param len          number of bytes to read
+ * @param retlen       pointer to variable to store the number of read bytes
+ * @param buf          the databuffer to put data
+ *
+ * Read with ecc
+*/
+static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
+       size_t *retlen, u_char *buf)
+{
+       struct mtd_oob_ops ops = {
+               .len    = len,
+               .ooblen = 0,
+               .datbuf = buf,
+               .oobbuf = NULL,
+       };
+       int ret;
+
+       onenand_get_device(mtd, FL_READING);
+       ret = onenand_read_ops_nolock(mtd, from, &ops);
        onenand_release_device(mtd);
 
-       *retlen = read;
+       *retlen = ops.retlen;
        return ret;
 }
 
 /**
- * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band
+ * onenand_read_oob - [MTD Interface] Read main and/or out-of-band
  * @param mtd:         MTD device structure
  * @param from:                offset to read from
  * @param ops:         oob operation description structure
+
+ * Read main and/or out-of-band
  */
 static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
                            struct mtd_oob_ops *ops)
 {
+       int ret;
+
        switch (ops->mode) {
        case MTD_OOB_PLACE:
        case MTD_OOB_AUTO:
@@ -972,8 +1070,15 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
        default:
                return -EINVAL;
        }
-       return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
-                                  &ops->oobretlen, ops->oobbuf, ops->mode);
+
+       onenand_get_device(mtd, FL_READING);
+       if (ops->datbuf)
+               ret = onenand_read_ops_nolock(mtd, from, ops);
+       else
+               ret = onenand_read_oob_nolock(mtd, from, ops);
+       onenand_release_device(mtd);
+
+       return ret;
 }
 
 /**
@@ -1079,7 +1184,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
                /* Read more? */
                if (read < len) {
                        /* Update Page size */
-                       from += mtd->writesize;
+                       from += this->writesize;
                        column = 0;
                }
        }
@@ -1097,7 +1202,6 @@ int onenand_bbt_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
- *
  */
 static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
 {
@@ -1125,7 +1229,6 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
  * @param buf          the databuffer to verify
  * @param addr         offset to read from
  * @param len          number of bytes to read and compare
- *
  */
 static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
 {
@@ -1135,12 +1238,12 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
        int thislen, column;
 
        while (len != 0) {
-               thislen = min_t(int, mtd->writesize, len);
-               column = addr & (mtd->writesize - 1);
-               if (column + thislen > mtd->writesize)
-                       thislen = mtd->writesize - column;
+               thislen = min_t(int, this->writesize, len);
+               column = addr & (this->writesize - 1);
+               if (column + thislen > this->writesize)
+                       thislen = this->writesize - column;
 
-               this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
+               this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
 
                onenand_update_bufferram(mtd, addr, 0);
 
@@ -1171,50 +1274,101 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
 #define NOTALIGNED(x)  ((x & (this->subpagesize - 1)) != 0)
 
 /**
- * onenand_write - [MTD Interface] write buffer to FLASH
+ * 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;
+       unsigned int i;
+
+       free = this->ecclayout->oobfree;
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
+               if (writecol >= lastgap)
+                       writecol += free->offset - lastgap;
+               if (writeend >= lastgap)
+                       writeend += free->offset - lastgap;
+               lastgap = free->offset + free->length;
+       }
+       free = this->ecclayout->oobfree;
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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;
+               } else if (column == 0)
+                       break;
+       }
+       return 0;
+}
+
+/**
+ * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
  * @param mtd          MTD device structure
  * @param to           offset to write to
- * @param len          number of bytes to write
- * @param retlen       pointer to variable to store the number of written bytes
- * @param buf          the data to write
+ * @param ops          oob operation description structure
  *
- * Write with ECC
+ * Write main and/or oob with ECC
  */
-static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
-       size_t *retlen, const u_char *buf)
+static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
+                               struct mtd_oob_ops *ops)
 {
        struct onenand_chip *this = mtd->priv;
-       int written = 0;
+       int written = 0, column, thislen, subpage;
+       int oobwritten = 0, oobcolumn, thisooblen, oobsize;
+       size_t len = ops->len;
+       size_t ooblen = ops->ooblen;
+       const u_char *buf = ops->datbuf;
+       const u_char *oob = ops->oobbuf;
+       u_char *oobbuf;
        int ret = 0;
-       int column, subpage;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
 
        /* Initialize retlen, in case of early exit */
-       *retlen = 0;
+       ops->retlen = 0;
+       ops->oobretlen = 0;
 
        /* Do not allow writes past end of device */
        if (unlikely((to + len) > mtd->size)) {
-               printk(KERN_ERR "onenand_write: Attempt write to past end of device\n");
+               printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
                return -EINVAL;
        }
 
        /* Reject writes, which are not page aligned */
         if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
-                printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n");
+                printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
                 return -EINVAL;
         }
 
-       column = to & (mtd->writesize - 1);
+       if (ops->mode == MTD_OOB_AUTO)
+               oobsize = this->ecclayout->oobavail;
+       else
+               oobsize = mtd->oobsize;
 
-       /* Grab the lock and see if the device is available */
-       onenand_get_device(mtd, FL_WRITING);
+       oobcolumn = to & (mtd->oobsize - 1);
+
+       column = to & (mtd->writesize - 1);
 
        /* Loop until all data write */
        while (written < len) {
-               int thislen = min_t(int, mtd->writesize - column, len - written);
                u_char *wbuf = (u_char *) buf;
 
+               thislen = min_t(int, mtd->writesize - column, len - written);
+               thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
+
                cond_resched();
 
                this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
@@ -1228,7 +1382,25 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
                }
 
                this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
-               this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
+
+               if (oob) {
+                       oobbuf = this->oob_buf;
+
+                       /* We send data to spare ram with oobsize
+                        * to prevent byte access */
+                       memset(oobbuf, 0xff, mtd->oobsize);
+                       if (ops->mode == MTD_OOB_AUTO)
+                               onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
+                       else
+                               memcpy(oobbuf + oobcolumn, oob, thisooblen);
+
+                       oobwritten += thisooblen;
+                       oob += thisooblen;
+                       oobcolumn = 0;
+               } else
+                       oobbuf = (u_char *) ffchars;
+
+               this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
 
                this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
 
@@ -1236,16 +1408,20 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
 
                /* In partial page write we don't update bufferram */
                onenand_update_bufferram(mtd, to, !ret && !subpage);
+               if (ONENAND_IS_2PLANE(this)) {
+                       ONENAND_SET_BUFFERRAM1(this);
+                       onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
+               }
 
                if (ret) {
-                       printk(KERN_ERR "onenand_write: write filaed %d\n", ret);
+                       printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
                        break;
                }
 
                /* Only check verify write turn on */
                ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
                if (ret) {
-                       printk(KERN_ERR "onenand_write: verify failed %d\n", ret);
+                       printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
                        break;
                }
 
@@ -1262,54 +1438,14 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
        /* Deselect and wake up anyone waiting on the device */
        onenand_release_device(mtd);
 
-       *retlen = written;
+       ops->retlen = written;
 
        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;
-       unsigned int i;
-
-       free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
-               if (writecol >= lastgap)
-                       writecol += free->offset - lastgap;
-               if (writeend >= lastgap)
-                       writeend += free->offset - lastgap;
-               lastgap = free->offset + free->length;
-       }
-       free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, 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;
-               } else if (column == 0)
-                       break;
-       }
-       return 0;
-}
 
 /**
- * onenand_do_write_oob - [Internal] OneNAND write out-of-band
+ * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
  * @param mtd          MTD device structure
  * @param to           offset to write to
  * @param len          number of bytes to write
@@ -1319,18 +1455,23 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
  *
  * 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, mtd_oob_mode_t mode)
+static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
+                                   struct mtd_oob_ops *ops)
 {
        struct onenand_chip *this = mtd->priv;
        int column, ret = 0, oobsize;
        int written = 0;
        u_char *oobbuf;
+       size_t len = ops->ooblen;
+       const u_char *buf = ops->oobbuf;
+       mtd_oob_mode_t mode = ops->mode;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+       to += ops->ooboffs;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
 
        /* Initialize retlen, in case of early exit */
-       *retlen = 0;
+       ops->oobretlen = 0;
 
        if (mode == MTD_OOB_AUTO)
                oobsize = this->ecclayout->oobavail;
@@ -1340,13 +1481,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
        column = to & (mtd->oobsize - 1);
 
        if (unlikely(column >= oobsize)) {
-               printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n");
+               printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n");
                return -EINVAL;
        }
 
        /* For compatibility with NAND: Do not allow write past end of page */
        if (unlikely(column + len > oobsize)) {
-               printk(KERN_ERR "onenand_write_oob: "
+               printk(KERN_ERR "onenand_write_oob_nolock: "
                      "Attempt to write past end of page\n");
                return -EINVAL;
        }
@@ -1355,13 +1496,10 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
        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");
+               printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n");
                return -EINVAL;
        }
 
-       /* Grab the lock and see if the device is available */
-       onenand_get_device(mtd, FL_WRITING);
-
        oobbuf = this->oob_buf;
 
        /* Loop until all data write */
@@ -1384,16 +1522,20 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
                this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
 
                onenand_update_bufferram(mtd, to, 0);
+               if (ONENAND_IS_2PLANE(this)) {
+                       ONENAND_SET_BUFFERRAM1(this);
+                       onenand_update_bufferram(mtd, to + this->writesize, 0);
+               }
 
                ret = this->wait(mtd, FL_WRITING);
                if (ret) {
-                       printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret);
+                       printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret);
                        break;
                }
 
                ret = onenand_verify_oob(mtd, oobbuf, to);
                if (ret) {
-                       printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret);
+                       printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret);
                        break;
                }
 
@@ -1406,11 +1548,37 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
                column = 0;
        }
 
-       /* Deselect and wake up anyone waiting on the device */
-       onenand_release_device(mtd);
+       ops->oobretlen = written;
+
+       return ret;
+}
+
+/**
+ * onenand_write - [MTD Interface] write buffer to FLASH
+ * @param mtd          MTD device structure
+ * @param to           offset to write to
+ * @param len          number of bytes to write
+ * @param retlen       pointer to variable to store the number of written bytes
+ * @param buf          the data to write
+ *
+ * Write with ECC
+ */
+static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
+       size_t *retlen, const u_char *buf)
+{
+       struct mtd_oob_ops ops = {
+               .len    = len,
+               .ooblen = 0,
+               .datbuf = (u_char *) buf,
+               .oobbuf = NULL,
+       };
+       int ret;
 
-       *retlen = written;
+       onenand_get_device(mtd, FL_WRITING);
+       ret = onenand_write_ops_nolock(mtd, to, &ops);
+       onenand_release_device(mtd);
 
+       *retlen = ops.retlen;
        return ret;
 }
 
@@ -1423,6 +1591,8 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
 static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
                             struct mtd_oob_ops *ops)
 {
+       int ret;
+
        switch (ops->mode) {
        case MTD_OOB_PLACE:
        case MTD_OOB_AUTO:
@@ -1432,21 +1602,27 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
        default:
                return -EINVAL;
        }
-       return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
-                                   &ops->oobretlen, ops->oobbuf, ops->mode);
+
+       onenand_get_device(mtd, FL_WRITING);
+       if (ops->datbuf)
+               ret = onenand_write_ops_nolock(mtd, to, ops);
+       else
+               ret = onenand_write_oob_nolock(mtd, to, ops);
+       onenand_release_device(mtd);
+
+       return ret;
 }
 
 /**
- * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
  * @param mtd          MTD device structure
  * @param ofs          offset from device start
- * @param getchip      0, if the chip is already selected
  * @param allowbbt     1, if its allowed to access the bbt area
  *
  * Check, if the block is bad. Either by reading the bad block table or
  * calling of the scan function.
  */
-static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
 {
        struct onenand_chip *this = mtd->priv;
        struct bbm_info *bbm = this->bbm;
@@ -1507,7 +1683,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
                cond_resched();
 
                /* Check if we have a bad block, we do not erase bad blocks */
-               if (onenand_block_checkbad(mtd, addr, 0, 0)) {
+               if (onenand_block_isbad_nolock(mtd, addr, 0)) {
                        printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr);
                        instr->state = MTD_ERASE_FAILED;
                        goto erase_exit;
@@ -1571,11 +1747,16 @@ static void onenand_sync(struct mtd_info *mtd)
  */
 static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
 {
+       int ret;
+
        /* Check for invalid offset */
        if (ofs > mtd->size)
                return -EINVAL;
 
-       return onenand_block_checkbad(mtd, ofs, 1, 0);
+       onenand_get_device(mtd, FL_READING);
+       ret = onenand_block_isbad_nolock(mtd, ofs, 0);
+       onenand_release_device(mtd);
+       return ret;
 }
 
 /**
@@ -1591,7 +1772,12 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
        struct onenand_chip *this = mtd->priv;
        struct bbm_info *bbm = this->bbm;
        u_char buf[2] = {0, 0};
-       size_t retlen;
+       struct mtd_oob_ops ops = {
+               .mode = MTD_OOB_PLACE,
+               .ooblen = 2,
+               .oobbuf = buf,
+               .ooboffs = 0,
+       };
        int block;
 
        /* Get block number */
@@ -1601,7 +1787,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, MTD_OOB_PLACE);
+        return onenand_write_oob_nolock(mtd, ofs, &ops);
 }
 
 /**
@@ -1624,7 +1810,10 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
                return ret;
        }
 
-       return this->block_markbad(mtd, ofs);
+       onenand_get_device(mtd, FL_WRITING);
+       ret = this->block_markbad(mtd, ofs);
+       onenand_release_device(mtd);
+       return ret;
 }
 
 /**
@@ -1823,13 +2012,19 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf)
 {
        struct onenand_chip *this = mtd->priv;
+       struct mtd_oob_ops ops = {
+               .len    = len,
+               .ooblen = 0,
+               .datbuf = buf,
+               .oobbuf = NULL,
+       };
        int ret;
 
        /* Enter OTP access mode */
        this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
        this->wait(mtd, FL_OTPING);
 
-       ret = mtd->read(mtd, from, len, retlen, buf);
+       ret = onenand_read_ops_nolock(mtd, from, &ops);
 
        /* Exit OTP access mode */
        this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -1841,19 +2036,20 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
 /**
  * do_otp_write - [DEFAULT] Write OTP block area
  * @param mtd          MTD device structure
- * @param from         The offset to write
+ * @param to           The offset to write
  * @param len          number of bytes to write
  * @param retlen       pointer to variable to store the number of write bytes
  * @param buf          the databuffer to put/get data
  *
  * Write OTP block area.
  */
-static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len,
+static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, u_char *buf)
 {
        struct onenand_chip *this = mtd->priv;
        unsigned char *pbuf = buf;
        int ret;
+       struct mtd_oob_ops ops;
 
        /* Force buffer page aligned */
        if (len < mtd->writesize) {
@@ -1867,7 +2063,12 @@ static int do_otp_write(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 = mtd->write(mtd, from, len, retlen, pbuf);
+       ops.len = len;
+       ops.ooblen = 0;
+       ops.datbuf = pbuf;
+       ops.oobbuf = NULL;
+       ret = onenand_write_ops_nolock(mtd, to, &ops);
+       *retlen = ops.retlen;
 
        /* Exit OTP access mode */
        this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -1890,13 +2091,21 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf)
 {
        struct onenand_chip *this = mtd->priv;
+       struct mtd_oob_ops ops = {
+               .mode = MTD_OOB_PLACE,
+               .ooblen = len,
+               .oobbuf = buf,
+               .ooboffs = 0,
+       };
        int ret;
 
        /* Enter OTP access mode */
        this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
        this->wait(mtd, FL_OTPING);
 
-       ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
+       ret = onenand_write_oob_nolock(mtd, from, &ops);
+
+       *retlen = ops.oobretlen;
 
        /* Exit OTP access mode */
        this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -1943,13 +2152,16 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
        if (((mtd->writesize * otp_pages) - (from + len)) < 0)
                return 0;
 
+       onenand_get_device(mtd, FL_OTPING);
        while (len > 0 && otp_pages > 0) {
                if (!action) {  /* OTP Info functions */
                        struct otp_info *otpinfo;
 
                        len -= sizeof(struct otp_info);
-                       if (len <= 0)
-                               return -ENOSPC;
+                       if (len <= 0) {
+                               ret = -ENOSPC;
+                               break;
+                       }
 
                        otpinfo = (struct otp_info *) buf;
                        otpinfo->start = from;
@@ -1969,13 +2181,14 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
                        len -= size;
                        *retlen += size;
 
-                       if (ret < 0)
-                               return ret;
+                       if (ret)
+                               break;
                }
                otp_pages--;
        }
+       onenand_release_device(mtd);
 
-       return 0;
+       return ret;
 }
 
 /**
@@ -2107,6 +2320,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
  *
  * Check and set OneNAND features
  * - lock scheme
+ * - two plane
  */
 static void onenand_check_features(struct mtd_info *mtd)
 {
@@ -2118,19 +2332,35 @@ static void onenand_check_features(struct mtd_info *mtd)
        process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
 
        /* Lock scheme */
-       if (density >= ONENAND_DEVICE_DENSITY_1Gb) {
+       switch (density) {
+       case ONENAND_DEVICE_DENSITY_4Gb:
+               this->options |= ONENAND_HAS_2PLANE;
+
+       case ONENAND_DEVICE_DENSITY_2Gb:
+               /* 2Gb DDP don't have 2 plane */
+               if (!ONENAND_IS_DDP(this))
+                       this->options |= ONENAND_HAS_2PLANE;
+               this->options |= ONENAND_HAS_UNLOCK_ALL;
+
+       case ONENAND_DEVICE_DENSITY_1Gb:
                /* A-Die has all block unlock */
-               if (process) {
-                       printk(KERN_DEBUG "Chip support all block unlock\n");
+               if (process)
                        this->options |= ONENAND_HAS_UNLOCK_ALL;
-               }
-       } else {
-               /* Some OneNAND has continues lock scheme */
-               if (!process) {
-                       printk(KERN_DEBUG "Lock scheme is Continues Lock\n");
+               break;
+
+       default:
+               /* Some OneNAND has continuous lock scheme */
+               if (!process)
                        this->options |= ONENAND_HAS_CONT_LOCK;
-               }
+               break;
        }
+
+       if (this->options & ONENAND_HAS_CONT_LOCK)
+               printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
+       if (this->options & ONENAND_HAS_UNLOCK_ALL)
+               printk(KERN_DEBUG "Chip support all block unlock\n");
+       if (this->options & ONENAND_HAS_2PLANE)
+               printk(KERN_DEBUG "Chip has 2 plane\n");
 }
 
 /**
@@ -2154,7 +2384,7 @@ static void onenand_print_device_info(int device, int version)
                 (16 << density),
                 vcc ? "2.65/3.3" : "1.8",
                 device);
-       printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version);
+       printk(KERN_INFO "OneNAND version = 0x%04x\n", version);
 }
 
 static const struct onenand_manufacturers onenand_manuf_ids[] = {
@@ -2257,6 +2487,8 @@ static int onenand_probe(struct mtd_info *mtd)
        this->erase_shift = ffs(mtd->erasesize) - 1;
        this->page_shift = ffs(mtd->writesize) - 1;
        this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
+       /* It's real page size */
+       this->writesize = mtd->writesize;
 
        /* REVIST: Multichip handling */
 
@@ -2265,6 +2497,17 @@ static int onenand_probe(struct mtd_info *mtd)
        /* Check OneNAND features */
        onenand_check_features(mtd);
 
+       /*
+        * We emulate the 4KiB page and 256KiB erase block size
+        * But oobsize is still 64 bytes.
+        * It is only valid if you turn on 2X program support,
+        * Otherwise it will be ignored by compiler.
+        */
+       if (ONENAND_IS_2PLANE(this)) {
+               mtd->writesize <<= 1;
+               mtd->erasesize <<= 1;
+       }
+
        return 0;
 }
 
diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c
new file mode 100644 (file)
index 0000000..0d89ad5
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ *  linux/drivers/mtd/onenand/onenand_sim.c
+ *
+ *  The OneNAND simulator
+ *
+ *  Copyright © 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
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/onenand.h>
+
+#include <linux/io.h>
+
+#ifndef CONFIG_ONENAND_SIM_MANUFACTURER
+#define CONFIG_ONENAND_SIM_MANUFACTURER         0xec
+#endif
+#ifndef CONFIG_ONENAND_SIM_DEVICE_ID
+#define CONFIG_ONENAND_SIM_DEVICE_ID            0x04
+#endif
+#ifndef CONFIG_ONENAND_SIM_VERSION_ID
+#define CONFIG_ONENAND_SIM_VERSION_ID           0x1e
+#endif
+
+static int manuf_id    = CONFIG_ONENAND_SIM_MANUFACTURER;
+static int device_id   = CONFIG_ONENAND_SIM_DEVICE_ID;
+static int version_id  = CONFIG_ONENAND_SIM_VERSION_ID;
+
+struct onenand_flash {
+       void __iomem *base;
+       void __iomem *data;
+};
+
+#define ONENAND_CORE(flash)            (flash->data)
+#define ONENAND_CORE_SPARE(flash, this, offset)                                \
+       ((flash->data) + (this->chipsize) + (offset >> 5))
+
+#define ONENAND_MAIN_AREA(this, offset)                                        \
+       (this->base + ONENAND_DATARAM + offset)
+
+#define ONENAND_SPARE_AREA(this, offset)                               \
+       (this->base + ONENAND_SPARERAM + offset)
+
+#define ONENAND_GET_WP_STATUS(this)                                    \
+       (readw(this->base + ONENAND_REG_WP_STATUS))
+
+#define ONENAND_SET_WP_STATUS(v, this)                                 \
+       (writew(v, this->base + ONENAND_REG_WP_STATUS))
+
+/* It has all 0xff chars */
+#define MAX_ONENAND_PAGESIZE           (2048 + 64)
+static unsigned char *ffchars;
+
+static struct mtd_partition os_partitions[] = {
+       {
+               .name           = "OneNAND simulator partition",
+               .offset         = 0,
+               .size           = MTDPART_SIZ_FULL,
+       },
+};
+
+/*
+ * OneNAND simulator mtd
+ */
+struct onenand_info {
+       struct mtd_info         mtd;
+       struct mtd_partition    *parts;
+       struct onenand_chip     onenand;
+       struct onenand_flash    flash;
+};
+
+static struct onenand_info *info;
+
+#define DPRINTK(format, args...)                                       \
+do {                                                                   \
+       printk(KERN_DEBUG "%s[%d]: " format "\n", __func__,             \
+                          __LINE__, ##args);                           \
+} while (0)
+
+/**
+ * onenand_lock_handle - Handle Lock scheme
+ * @param this         OneNAND device structure
+ * @param cmd          The command to be sent
+ *
+ * Send lock command to OneNAND device.
+ * The lock scheme is depends on chip type.
+ */
+static void onenand_lock_handle(struct onenand_chip *this, int cmd)
+{
+       int block_lock_scheme;
+       int status;
+
+       status = ONENAND_GET_WP_STATUS(this);
+       block_lock_scheme = !(this->options & ONENAND_HAS_CONT_LOCK);
+
+       switch (cmd) {
+       case ONENAND_CMD_UNLOCK:
+               if (block_lock_scheme)
+                       ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
+               else
+                       ONENAND_SET_WP_STATUS(status | ONENAND_WP_US, this);
+               break;
+
+       case ONENAND_CMD_LOCK:
+               if (block_lock_scheme)
+                       ONENAND_SET_WP_STATUS(ONENAND_WP_LS, this);
+               else
+                       ONENAND_SET_WP_STATUS(status | ONENAND_WP_LS, this);
+               break;
+
+       case ONENAND_CMD_LOCK_TIGHT:
+               if (block_lock_scheme)
+                       ONENAND_SET_WP_STATUS(ONENAND_WP_LTS, this);
+               else
+                       ONENAND_SET_WP_STATUS(status | ONENAND_WP_LTS, this);
+               break;
+
+       default:
+               break;
+       }
+}
+
+/**
+ * onenand_bootram_handle - Handle BootRAM area
+ * @param this         OneNAND device structure
+ * @param cmd          The command to be sent
+ *
+ * Emulate BootRAM area. It is possible to do basic operation using BootRAM.
+ */
+static void onenand_bootram_handle(struct onenand_chip *this, int cmd)
+{
+       switch (cmd) {
+       case ONENAND_CMD_READID:
+               writew(manuf_id, this->base);
+               writew(device_id, this->base + 2);
+               writew(version_id, this->base + 4);
+               break;
+
+       default:
+               /* REVIST: Handle other commands */
+               break;
+       }
+}
+
+/**
+ * onenand_update_interrupt - Set interrupt register
+ * @param this         OneNAND device structure
+ * @param cmd          The command to be sent
+ *
+ * Update interrupt register. The status is depends on command.
+ */
+static void onenand_update_interrupt(struct onenand_chip *this, int cmd)
+{
+       int interrupt = ONENAND_INT_MASTER;
+
+       switch (cmd) {
+       case ONENAND_CMD_READ:
+       case ONENAND_CMD_READOOB:
+               interrupt |= ONENAND_INT_READ;
+               break;
+
+       case ONENAND_CMD_PROG:
+       case ONENAND_CMD_PROGOOB:
+               interrupt |= ONENAND_INT_WRITE;
+               break;
+
+       case ONENAND_CMD_ERASE:
+               interrupt |= ONENAND_INT_ERASE;
+               break;
+
+       case ONENAND_CMD_RESET:
+               interrupt |= ONENAND_INT_RESET;
+               break;
+
+       default:
+               break;
+       }
+
+       writew(interrupt, this->base + ONENAND_REG_INTERRUPT);
+}
+
+/**
+ * onenand_check_overwrite - Check over-write if happend
+ * @param dest         The destination pointer
+ * @param src          The source pointer
+ * @param count                The length to be check
+ * @return             0 on same, otherwise 1
+ *
+ * Compare the source with destination
+ */
+static int onenand_check_overwrite(void *dest, void *src, size_t count)
+{
+       unsigned int *s = (unsigned int *) src;
+       unsigned int *d = (unsigned int *) dest;
+       int i;
+
+       count >>= 2;
+       for (i = 0; i < count; i++)
+               if ((*s++ ^ *d++) != 0)
+                       return 1;
+
+       return 0;
+}
+
+/**
+ * onenand_data_handle - Handle OneNAND Core and DataRAM
+ * @param this         OneNAND device structure
+ * @param cmd          The command to be sent
+ * @param dataram      Which dataram used
+ * @param offset       The offset to OneNAND Core
+ *
+ * Copy data from OneNAND Core to DataRAM (read)
+ * Copy data from DataRAM to OneNAND Core (write)
+ * Erase the OneNAND Core (erase)
+ */
+static void onenand_data_handle(struct onenand_chip *this, int cmd,
+                               int dataram, unsigned int offset)
+{
+       struct mtd_info *mtd = &info->mtd;
+       struct onenand_flash *flash = this->priv;
+       int main_offset, spare_offset;
+       void __iomem *src;
+       void __iomem *dest;
+       unsigned int i;
+
+       if (dataram) {
+               main_offset = mtd->writesize;
+               spare_offset = mtd->oobsize;
+       } else {
+               main_offset = 0;
+               spare_offset = 0;
+       }
+
+       switch (cmd) {
+       case ONENAND_CMD_READ:
+               src = ONENAND_CORE(flash) + offset;
+               dest = ONENAND_MAIN_AREA(this, main_offset);
+               memcpy(dest, src, mtd->writesize);
+               /* Fall through */
+
+       case ONENAND_CMD_READOOB:
+               src = ONENAND_CORE_SPARE(flash, this, offset);
+               dest = ONENAND_SPARE_AREA(this, spare_offset);
+               memcpy(dest, src, mtd->oobsize);
+               break;
+
+       case ONENAND_CMD_PROG:
+               src = ONENAND_MAIN_AREA(this, main_offset);
+               dest = ONENAND_CORE(flash) + offset;
+               /* To handle partial write */
+               for (i = 0; i < (1 << mtd->subpage_sft); i++) {
+                       int off = i * this->subpagesize;
+                       if (!memcmp(src + off, ffchars, this->subpagesize))
+                               continue;
+                       if (memcmp(dest + off, ffchars, this->subpagesize) &&
+                           onenand_check_overwrite(dest + off, src + off, this->subpagesize))
+                               printk(KERN_ERR "over-write happend at 0x%08x\n", offset);
+                       memcpy(dest + off, src + off, this->subpagesize);
+               }
+               /* Fall through */
+
+       case ONENAND_CMD_PROGOOB:
+               src = ONENAND_SPARE_AREA(this, spare_offset);
+               /* Check all data is 0xff chars */
+               if (!memcmp(src, ffchars, mtd->oobsize))
+                       break;
+
+               dest = ONENAND_CORE_SPARE(flash, this, offset);
+               if (memcmp(dest, ffchars, mtd->oobsize) &&
+                   onenand_check_overwrite(dest, src, mtd->oobsize))
+                       printk(KERN_ERR "OOB: over-write happend at 0x%08x\n",
+                              offset);
+               memcpy(dest, src, mtd->oobsize);
+               break;
+
+       case ONENAND_CMD_ERASE:
+               memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
+               memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
+                      (mtd->erasesize >> 5));
+               break;
+
+       default:
+               break;
+       }
+}
+
+/**
+ * onenand_command_handle - Handle command
+ * @param this         OneNAND device structure
+ * @param cmd          The command to be sent
+ *
+ * Emulate OneNAND command.
+ */
+static void onenand_command_handle(struct onenand_chip *this, int cmd)
+{
+       unsigned long offset = 0;
+       int block = -1, page = -1, bufferram = -1;
+       int dataram = 0;
+
+       switch (cmd) {
+       case ONENAND_CMD_UNLOCK:
+       case ONENAND_CMD_LOCK:
+       case ONENAND_CMD_LOCK_TIGHT:
+       case ONENAND_CMD_UNLOCK_ALL:
+               onenand_lock_handle(this, cmd);
+               break;
+
+       case ONENAND_CMD_BUFFERRAM:
+               /* Do nothing */
+               return;
+
+       default:
+               block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1);
+               if (block & (1 << ONENAND_DDP_SHIFT)) {
+                       block &= ~(1 << ONENAND_DDP_SHIFT);
+                       /* The half of chip block */
+                       block += this->chipsize >> (this->erase_shift + 1);
+               }
+               if (cmd == ONENAND_CMD_ERASE)
+                       break;
+
+               page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8);
+               page = (page >> ONENAND_FPA_SHIFT);
+               bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER);
+               bufferram >>= ONENAND_BSA_SHIFT;
+               bufferram &= ONENAND_BSA_DATARAM1;
+               dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0;
+               break;
+       }
+
+       if (block != -1)
+               offset += block << this->erase_shift;
+
+       if (page != -1)
+               offset += page << this->page_shift;
+
+       onenand_data_handle(this, cmd, dataram, offset);
+
+       onenand_update_interrupt(this, cmd);
+}
+
+/**
+ * onenand_writew - [OneNAND Interface] Emulate write operation
+ * @param value                value to write
+ * @param addr         address to write
+ *
+ * Write OneNAND register with value
+ */
+static void onenand_writew(unsigned short value, void __iomem * addr)
+{
+       struct onenand_chip *this = info->mtd.priv;
+
+       /* BootRAM handling */
+       if (addr < this->base + ONENAND_DATARAM) {
+               onenand_bootram_handle(this, value);
+               return;
+       }
+       /* Command handling */
+       if (addr == this->base + ONENAND_REG_COMMAND)
+               onenand_command_handle(this, value);
+
+       writew(value, addr);
+}
+
+/**
+ * flash_init - Initialize OneNAND simulator
+ * @param flash                OneNAND simulaotr data strucutres
+ *
+ * Initialize OneNAND simulator.
+ */
+static int __init flash_init(struct onenand_flash *flash)
+{
+       int density, size;
+       int buffer_size;
+
+       flash->base = kzalloc(131072, GFP_KERNEL);
+       if (!flash->base) {
+               printk(KERN_ERR "Unable to allocate base address.\n");
+               return -ENOMEM;
+       }
+
+       density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       size = ((16 << 20) << density);
+
+       ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
+       if (!ONENAND_CORE(flash)) {
+               printk(KERN_ERR "Unable to allocate nand core address.\n");
+               kfree(flash->base);
+               return -ENOMEM;
+       }
+
+       memset(ONENAND_CORE(flash), 0xff, size + (size >> 5));
+
+       /* Setup registers */
+       writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
+       writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
+       writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
+
+       if (density < 2)
+               buffer_size = 0x0400;   /* 1KiB page */
+       else
+               buffer_size = 0x0800;   /* 2KiB page */
+       writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE);
+
+       return 0;
+}
+
+/**
+ * flash_exit - Clean up OneNAND simulator
+ * @param flash                OneNAND simulaotr data strucutres
+ *
+ * Clean up OneNAND simulator.
+ */
+static void flash_exit(struct onenand_flash *flash)
+{
+       vfree(ONENAND_CORE(flash));
+       kfree(flash->base);
+       kfree(flash);
+}
+
+static int __init onenand_sim_init(void)
+{
+       /* Allocate all 0xff chars pointer */
+       ffchars = kmalloc(MAX_ONENAND_PAGESIZE, GFP_KERNEL);
+       if (!ffchars) {
+               printk(KERN_ERR "Unable to allocate ff chars.\n");
+               return -ENOMEM;
+       }
+       memset(ffchars, 0xff, MAX_ONENAND_PAGESIZE);
+
+       /* Allocate OneNAND simulator mtd pointer */
+       info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
+       if (!info) {
+               printk(KERN_ERR "Unable to allocate core structures.\n");
+               kfree(ffchars);
+               return -ENOMEM;
+       }
+
+       /* Override write_word function */
+       info->onenand.write_word = onenand_writew;
+
+       if (flash_init(&info->flash)) {
+               printk(KERN_ERR "Unable to allocat flash.\n");
+               kfree(ffchars);
+               kfree(info);
+               return -ENOMEM;
+       }
+
+       info->parts = os_partitions;
+
+       info->onenand.base = info->flash.base;
+       info->onenand.priv = &info->flash;
+
+       info->mtd.name = "OneNAND simulator";
+       info->mtd.priv = &info->onenand;
+       info->mtd.owner = THIS_MODULE;
+
+       if (onenand_scan(&info->mtd, 1)) {
+               flash_exit(&info->flash);
+               kfree(ffchars);
+               kfree(info);
+               return -ENXIO;
+       }
+
+       add_mtd_partitions(&info->mtd, info->parts, ARRAY_SIZE(os_partitions));
+
+       return 0;
+}
+
+static void __exit onenand_sim_exit(void)
+{
+       struct onenand_chip *this = info->mtd.priv;
+       struct onenand_flash *flash = this->priv;
+
+       onenand_release(&info->mtd);
+       flash_exit(flash);
+       kfree(ffchars);
+       kfree(info);
+}
+
+module_init(onenand_sim_init);
+module_exit(onenand_sim_exit);
+
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("The OneNAND flash simulator");
+MODULE_LICENSE("GPL");
index 006c03a..823fba4 100644 (file)
@@ -779,10 +779,8 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        else {
                if (!mtd->erasesize) {
                        printk(KERN_WARNING PREFIX "please provide block_size");
-                       kfree(part);
-                       return;
-               }
-               else
+                       goto out;
+               } else
                        part->block_size = mtd->erasesize;
        }
 
@@ -804,7 +802,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
                if (!add_mtd_blktrans_dev((void*)part))
                        return;
        }
-
+out:
        kfree(part);
 }
 
index 94ee549..29c41ee 100644 (file)
@@ -1314,11 +1314,10 @@ static int paranoid_check_si(const struct ubi_device *ubi,
         * Make sure that all the physical eraseblocks are in one of the lists
         * or trees.
         */
-       buf = kmalloc(ubi->peb_count, GFP_KERNEL);
+       buf = kzalloc(ubi->peb_count, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
-       memset(buf, 1, ubi->peb_count);
        for (pnum = 0; pnum < ubi->peb_count; pnum++) {
                err = ubi_io_is_bad(ubi, pnum);
                if (err < 0) {
@@ -1326,28 +1325,28 @@ static int paranoid_check_si(const struct ubi_device *ubi,
                        return err;
                }
                else if (err)
-                       buf[pnum] = 0;
+                       buf[pnum] = 1;
        }
 
        ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb)
                ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb)
-                       buf[seb->pnum] = 0;
+                       buf[seb->pnum] = 1;
 
        list_for_each_entry(seb, &si->free, u.list)
-               buf[seb->pnum] = 0;
+               buf[seb->pnum] = 1;
 
        list_for_each_entry(seb, &si->corr, u.list)
-               buf[seb->pnum] = 0;
+               buf[seb->pnum] = 1;
 
        list_for_each_entry(seb, &si->erase, u.list)
-               buf[seb->pnum] = 0;
+               buf[seb->pnum] = 1;
 
        list_for_each_entry(seb, &si->alien, u.list)
-               buf[seb->pnum] = 0;
+               buf[seb->pnum] = 1;
 
        err = 0;
        for (pnum = 0; pnum < ubi->peb_count; pnum++)
-               if (buf[pnum]) {
+               if (!buf[pnum]) {
                        ubi_err("PEB %d is not referred", pnum);
                        err = 1;
                }
index f9eed6d..bb02b39 100644 (file)
@@ -1225,6 +1225,14 @@ config JFFS2_FS_WRITEBUFFER
            - NOR flash with transparent ECC
            - DataFlash
 
+config JFFS2_FS_WBUF_VERIFY
+       bool "Verify JFFS2 write-buffer reads"
+       depends on JFFS2_FS_WRITEBUFFER
+       default n
+       help
+         This causes JFFS2 to read back every page written through the
+         write-buffer, and check for errors.
+
 config JFFS2_SUMMARY
        bool "JFFS2 summary support (EXPERIMENTAL)"
        depends on JFFS2_FS && EXPERIMENTAL
@@ -1295,52 +1303,71 @@ config JFFS2_ZLIB
        select ZLIB_DEFLATE
        depends on JFFS2_FS
        default y
-        help
-          Zlib is designed to be a free, general-purpose, legally unencumbered,
-          lossless data-compression library for use on virtually any computer
-          hardware and operating system. See <http://www.gzip.org/zlib/> for
-          further information.
+       help
+         Zlib is designed to be a free, general-purpose, legally unencumbered,
+         lossless data-compression library for use on virtually any computer
+         hardware and operating system. See <http://www.gzip.org/zlib/> for
+         further information.
 
-          Say 'Y' if unsure.
+         Say 'Y' if unsure.
+
+config JFFS2_LZO
+       bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS
+       select LZO_COMPRESS
+       select LZO_DECOMPRESS
+       depends on JFFS2_FS
+       default n
+       help
+         minilzo-based compression. Generally works better than Zlib.
+
+         This feature was added in July, 2007. Say 'N' if you need
+         compatibility with older bootloaders or kernels.
 
 config JFFS2_RTIME
        bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
        depends on JFFS2_FS
        default y
-        help
-          Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
+       help
+         Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
 
 config JFFS2_RUBIN
        bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS
        depends on JFFS2_FS
        default n
-        help
-          RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
+       help
+         RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
 
 choice
-        prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
-        default JFFS2_CMODE_PRIORITY
-        depends on JFFS2_FS
-        help
-          You can set here the default compression mode of JFFS2 from
-          the available compression modes. Don't touch if unsure.
+       prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
+       default JFFS2_CMODE_PRIORITY
+       depends on JFFS2_FS
+       help
+         You can set here the default compression mode of JFFS2 from
+         the available compression modes. Don't touch if unsure.
 
 config JFFS2_CMODE_NONE
-        bool "no compression"
-        help
-          Uses no compression.
+       bool "no compression"
+       help
+         Uses no compression.
 
 config JFFS2_CMODE_PRIORITY
-        bool "priority"
-        help
-          Tries the compressors in a predefined order and chooses the first
-          successful one.
+       bool "priority"
+       help
+         Tries the compressors in a predefined order and chooses the first
+         successful one.
 
 config JFFS2_CMODE_SIZE
-        bool "size (EXPERIMENTAL)"
-        help
-          Tries all compressors and chooses the one which has the smallest
-          result.
+       bool "size (EXPERIMENTAL)"
+       help
+         Tries all compressors and chooses the one which has the smallest
+         result.
+
+config JFFS2_CMODE_FAVOURLZO
+       bool "Favour LZO"
+       help
+         Tries all compressors and chooses the one which has the smallest
+         result but gives some preference to LZO (which has faster
+         decompression) at the expense of size.
 
 endchoice
 
index c32b241..60e5d49 100644 (file)
@@ -17,4 +17,5 @@ jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL)    += acl.o
 jffs2-$(CONFIG_JFFS2_RUBIN)    += compr_rubin.o
 jffs2-$(CONFIG_JFFS2_RTIME)    += compr_rtime.o
 jffs2-$(CONFIG_JFFS2_ZLIB)     += compr_zlib.o
+jffs2-$(CONFIG_JFFS2_LZO)      += compr_lzo.o
 jffs2-$(CONFIG_JFFS2_SUMMARY)   += summary.o
index 65b3a1b..8ec9323 100644 (file)
@@ -176,7 +176,7 @@ static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct
        spin_unlock(&inode->i_lock);
 }
 
-static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
+struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
 {
        struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
        struct posix_acl *acl;
@@ -247,8 +247,13 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
                        if (rc < 0)
                                return rc;
                        if (inode->i_mode != mode) {
-                               inode->i_mode = mode;
-                               jffs2_dirty_inode(inode);
+                               struct iattr attr;
+
+                               attr.ia_valid = ATTR_MODE;
+                               attr.ia_mode = mode;
+                               rc = jffs2_do_setattr(inode, &attr);
+                               if (rc < 0)
+                                       return rc;
                        }
                        if (rc == 0)
                                acl = NULL;
@@ -307,22 +312,16 @@ int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
        return generic_permission(inode, mask, jffs2_check_acl);
 }
 
-int jffs2_init_acl(struct inode *inode, struct inode *dir)
+int jffs2_init_acl(struct inode *inode, struct posix_acl *acl)
 {
        struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-       struct posix_acl *acl = NULL, *clone;
+       struct posix_acl *clone;
        mode_t mode;
        int rc = 0;
 
        f->i_acl_access = JFFS2_ACL_NOT_CACHED;
        f->i_acl_default = JFFS2_ACL_NOT_CACHED;
-       if (!S_ISLNK(inode->i_mode)) {
-               acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
-               if (IS_ERR(acl))
-                       return PTR_ERR(acl);
-               if (!acl)
-                       inode->i_mode &= ~current->fs->umask;
-       }
+
        if (acl) {
                if (S_ISDIR(inode->i_mode)) {
                        rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
index c84378c..90a2dbf 100644 (file)
@@ -28,9 +28,10 @@ struct jffs2_acl_header {
 
 #define JFFS2_ACL_NOT_CACHED ((void *)-1)
 
+extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
 extern int jffs2_permission(struct inode *, int, struct nameidata *);
 extern int jffs2_acl_chmod(struct inode *);
-extern int jffs2_init_acl(struct inode *, struct inode *);
+extern int jffs2_init_acl(struct inode *, struct posix_acl *);
 extern void jffs2_clear_acl(struct jffs2_inode_info *);
 
 extern struct xattr_handler jffs2_acl_access_xattr_handler;
@@ -38,6 +39,7 @@ extern struct xattr_handler jffs2_acl_default_xattr_handler;
 
 #else
 
+#define jffs2_get_acl(inode, type)     (NULL)
 #define jffs2_permission NULL
 #define jffs2_acl_chmod(inode)         (0)
 #define jffs2_init_acl(inode,dir)      (0)
index 504643f..d568ae8 100644 (file)
@@ -23,8 +23,8 @@ static int jffs2_garbage_collect_thread(void *);
 void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
 {
        spin_lock(&c->erase_completion_lock);
-        if (c->gc_task && jffs2_thread_should_wake(c))
-                send_sig(SIGHUP, c->gc_task, 1);
+       if (c->gc_task && jffs2_thread_should_wake(c))
+               send_sig(SIGHUP, c->gc_task, 1);
        spin_unlock(&c->erase_completion_lock);