]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge git://git.infradead.org/mtd-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 9 Jan 2009 20:36:45 +0000 (12:36 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 9 Jan 2009 20:37:15 +0000 (12:37 -0800)
* git://git.infradead.org/mtd-2.6: (67 commits)
  [MTD] [MAPS] Fix printk format warning in nettel.c
  [MTD] [NAND] add cmdline parsing (mtdparts=) support to cafe_nand
  [MTD] CFI: remove major/minor version check for command set 0x0002
  [MTD] [NAND] ndfc driver
  [MTD] [TESTS] Fix some size_t printk format warnings
  [MTD] LPDDR Makefile and KConfig
  [MTD] LPDDR extended physmap driver to support LPDDR flash
  [MTD] LPDDR added new pfow_base parameter
  [MTD] LPDDR Command set driver
  [MTD] LPDDR PFOW definition
  [MTD] LPDDR QINFO records definitions
  [MTD] LPDDR qinfo probing.
  [MTD] [NAND] pxa3xx: convert from ns to clock ticks more accurately
  [MTD] [NAND] pxa3xx: fix non-page-aligned reads
  [MTD] [NAND] fix nandsim sched.h references
  [MTD] [NAND] alauda: use USB API functions rather than constants
  [MTD] struct device - replace bus_id with dev_name(), dev_set_name()
  [MTD] fix m25p80 64-bit divisions
  [MTD] fix dataflash 64-bit divisions
  [MTD] [NAND] Set the fsl elbc ECCM according the settings in bootloader.
  ...

Fixed up trivial debug conflicts in drivers/mtd/devices/{m25p80.c,mtd_dataflash.c}

89 files changed:
Documentation/powerpc/dts-bindings/4xx/ndfc.txt [new file with mode: 0644]
arch/arm/mach-pxa/corgi.c
arch/arm/mach-pxa/poodle.c
arch/arm/mach-pxa/spitz.c
drivers/mtd/Kconfig
drivers/mtd/Makefile
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/cfi_cmdset_0020.c
drivers/mtd/chips/fwh_lock.h
drivers/mtd/devices/lart.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/ftl.c
drivers/mtd/inftlcore.c
drivers/mtd/inftlmount.c
drivers/mtd/lpddr/Kconfig [new file with mode: 0644]
drivers/mtd/lpddr/Makefile [new file with mode: 0644]
drivers/mtd/lpddr/lpddr_cmds.c [new file with mode: 0644]
drivers/mtd/lpddr/qinfo_probe.c [new file with mode: 0644]
drivers/mtd/maps/Kconfig
drivers/mtd/maps/alchemy-flash.c
drivers/mtd/maps/amd76xrom.c
drivers/mtd/maps/cfi_flagadm.c
drivers/mtd/maps/ck804xrom.c
drivers/mtd/maps/dbox2-flash.c
drivers/mtd/maps/edb7312.c
drivers/mtd/maps/esb2rom.c
drivers/mtd/maps/fortunet.c
drivers/mtd/maps/h720x-flash.c
drivers/mtd/maps/ichxrom.c
drivers/mtd/maps/impa7.c
drivers/mtd/maps/ipaq-flash.c
drivers/mtd/maps/mbx860.c
drivers/mtd/maps/nettel.c
drivers/mtd/maps/octagon-5066.c
drivers/mtd/maps/physmap.c
drivers/mtd/maps/pmcmsp-flash.c
drivers/mtd/maps/redwood.c
drivers/mtd/maps/rpxlite.c
drivers/mtd/maps/sbc8240.c
drivers/mtd/maps/scb2_flash.c
drivers/mtd/maps/sharpsl-flash.c
drivers/mtd/maps/tqm8xxl.c
drivers/mtd/maps/uclinux.c
drivers/mtd/maps/vmax301.c
drivers/mtd/maps/wr_sbc82xx_flash.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdoops.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/alauda.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_bbt.c
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/ndfc.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/mtd/nand/sharpsl.c
drivers/mtd/nftlcore.c
drivers/mtd/nftlmount.c
drivers/mtd/onenand/onenand_base.c
drivers/mtd/rfd_ftl.c
drivers/mtd/ssfdc.c
drivers/mtd/tests/Makefile [new file with mode: 0644]
drivers/mtd/tests/mtd_oobtest.c [new file with mode: 0644]
drivers/mtd/tests/mtd_pagetest.c [new file with mode: 0644]
drivers/mtd/tests/mtd_readtest.c [new file with mode: 0644]
drivers/mtd/tests/mtd_speedtest.c [new file with mode: 0644]
drivers/mtd/tests/mtd_stresstest.c [new file with mode: 0644]
drivers/mtd/tests/mtd_subpagetest.c [new file with mode: 0644]
drivers/mtd/tests/mtd_torturetest.c [new file with mode: 0644]
drivers/mtd/ubi/build.c
drivers/mtd/ubi/gluebi.c
fs/jffs2/compr_rubin.c
fs/jffs2/erase.c
include/linux/mtd/cfi.h
include/linux/mtd/ftl.h
include/linux/mtd/map.h
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
include/linux/mtd/partitions.h
include/linux/mtd/pfow.h [new file with mode: 0644]
include/linux/mtd/physmap.h
include/linux/mtd/qinfo.h [new file with mode: 0644]
include/linux/mtd/sharpsl.h [new file with mode: 0644]

diff --git a/Documentation/powerpc/dts-bindings/4xx/ndfc.txt b/Documentation/powerpc/dts-bindings/4xx/ndfc.txt
new file mode 100644 (file)
index 0000000..869f0b5
--- /dev/null
@@ -0,0 +1,39 @@
+AMCC NDFC (NanD Flash Controller)
+
+Required properties:
+- compatible : "ibm,ndfc".
+- reg : should specify chip select and size used for the chip (0x2000).
+
+Optional properties:
+- ccr : NDFC config and control register value (default 0).
+- bank-settings : NDFC bank configuration register value (default 0).
+
+Notes:
+- partition(s) - follows the OF MTD standard for partitions
+
+Example:
+
+ndfc@1,0 {
+       compatible = "ibm,ndfc";
+       reg = <0x00000001 0x00000000 0x00002000>;
+       ccr = <0x00001000>;
+       bank-settings = <0x80002222>;
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       nand {
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               partition@0 {
+                       label = "kernel";
+                       reg = <0x00000000 0x00200000>;
+               };
+               partition@200000 {
+                       label = "root";
+                       reg = <0x00200000 0x03E00000>;
+               };
+       };
+};
+
+
index c5e28a46b2929cc92133073608eaf84606971ca6..a8d91b6c136b8daba787e724a16d725fb8553157 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 #include <linux/spi/corgi_lcd.h>
+#include <linux/mtd/sharpsl.h>
 #include <video/w100fb.h>
 
 #include <asm/setup.h>
@@ -542,6 +543,55 @@ err_free_1:
 static inline void corgi_init_spi(void) {}
 #endif
 
+static struct mtd_partition sharpsl_nand_partitions[] = {
+       {
+               .name = "System Area",
+               .offset = 0,
+               .size = 7 * 1024 * 1024,
+       },
+       {
+               .name = "Root Filesystem",
+               .offset = 7 * 1024 * 1024,
+               .size = 25 * 1024 * 1024,
+       },
+       {
+               .name = "Home Filesystem",
+               .offset = MTDPART_OFS_APPEND,
+               .size = MTDPART_SIZ_FULL,
+       },
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr sharpsl_bbt = {
+       .options = 0,
+       .offs = 4,
+       .len = 2,
+       .pattern = scan_ff_pattern
+};
+
+static struct sharpsl_nand_platform_data sharpsl_nand_platform_data = {
+       .badblock_pattern       = &sharpsl_bbt,
+       .partitions             = sharpsl_nand_partitions,
+       .nr_partitions          = ARRAY_SIZE(sharpsl_nand_partitions),
+};
+
+static struct resource sharpsl_nand_resources[] = {
+       {
+               .start  = 0x0C000000,
+               .end    = 0x0C000FFF,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device sharpsl_nand_device = {
+       .name           = "sharpsl-nand",
+       .id             = -1,
+       .resource       = sharpsl_nand_resources,
+       .num_resources  = ARRAY_SIZE(sharpsl_nand_resources),
+       .dev.platform_data      = &sharpsl_nand_platform_data,
+};
+
 static struct mtd_partition sharpsl_rom_parts[] = {
        {
                .name   ="Boot PROM Filesystem",
@@ -577,6 +627,7 @@ static struct platform_device *devices[] __initdata = {
        &corgifb_device,
        &corgikbd_device,
        &corgiled_device,
+       &sharpsl_nand_device,
        &sharpsl_rom_device,
 };
 
@@ -617,6 +668,9 @@ static void __init corgi_init(void)
 
        platform_scoop_config = &corgi_pcmcia_config;
 
+       if (machine_is_husky())
+               sharpsl_nand_partitions[1].size = 53 * 1024 * 1024;
+
        platform_add_devices(devices, ARRAY_SIZE(devices));
 }
 
index ae88855bf97461b1851b78b0a1c64786740880f1..f9093beba752295a52cac1559b49243da7283d14 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
+#include <linux/mtd/sharpsl.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
@@ -414,6 +415,55 @@ static struct pxafb_mach_info poodle_fb_info = {
        .lcd_conn       = LCD_COLOR_TFT_16BPP,
 };
 
+static struct mtd_partition sharpsl_nand_partitions[] = {
+       {
+               .name = "System Area",
+               .offset = 0,
+               .size = 7 * 1024 * 1024,
+       },
+       {
+               .name = "Root Filesystem",
+               .offset = 7 * 1024 * 1024,
+               .size = 22 * 1024 * 1024,
+       },
+       {
+               .name = "Home Filesystem",
+               .offset = MTDPART_OFS_APPEND,
+               .size = MTDPART_SIZ_FULL,
+       },
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr sharpsl_bbt = {
+       .options = 0,
+       .offs = 4,
+       .len = 2,
+       .pattern = scan_ff_pattern
+};
+
+static struct sharpsl_nand_platform_data sharpsl_nand_platform_data = {
+       .badblock_pattern       = &sharpsl_bbt,
+       .partitions             = sharpsl_nand_partitions,
+       .nr_partitions          = ARRAY_SIZE(sharpsl_nand_partitions),
+};
+
+static struct resource sharpsl_nand_resources[] = {
+       {
+               .start  = 0x0C000000,
+               .end    = 0x0C000FFF,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device sharpsl_nand_device = {
+       .name           = "sharpsl-nand",
+       .id             = -1,
+       .resource       = sharpsl_nand_resources,
+       .num_resources  = ARRAY_SIZE(sharpsl_nand_resources),
+       .dev.platform_data      = &sharpsl_nand_platform_data,
+};
+
 static struct mtd_partition sharpsl_rom_parts[] = {
        {
                .name   ="Boot PROM Filesystem",
@@ -447,6 +497,7 @@ static struct platform_device sharpsl_rom_device = {
 static struct platform_device *devices[] __initdata = {
        &poodle_locomo_device,
        &poodle_scoop_device,
+       &sharpsl_nand_device,
        &sharpsl_rom_device,
 };
 
index 7299d87a1cb31f9b33d0ae9deadc2d9e30eab9a3..6d447c9ce8abfcadb2743f11330a48ab93d7d125 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 #include <linux/spi/corgi_lcd.h>
+#include <linux/mtd/sharpsl.h>
 
 #include <asm/setup.h>
 #include <asm/memory.h>
@@ -613,6 +614,54 @@ static struct pxafb_mach_info spitz_pxafb_info = {
        .lcd_conn       = LCD_COLOR_TFT_16BPP | LCD_ALTERNATE_MAPPING,
 };
 
+static struct mtd_partition sharpsl_nand_partitions[] = {
+       {
+               .name = "System Area",
+               .offset = 0,
+               .size = 7 * 1024 * 1024,
+       },
+       {
+               .name = "Root Filesystem",
+               .offset = 7 * 1024 * 1024,
+       },
+       {
+               .name = "Home Filesystem",
+               .offset = MTDPART_OFS_APPEND,
+               .size = MTDPART_SIZ_FULL,
+       },
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr sharpsl_bbt = {
+       .options = 0,
+       .offs = 4,
+       .len = 2,
+       .pattern = scan_ff_pattern
+};
+
+static struct sharpsl_nand_platform_data sharpsl_nand_platform_data = {
+       .badblock_pattern       = &sharpsl_bbt,
+       .partitions             = sharpsl_nand_partitions,
+       .nr_partitions          = ARRAY_SIZE(sharpsl_nand_partitions),
+};
+
+static struct resource sharpsl_nand_resources[] = {
+       {
+               .start  = 0x0C000000,
+               .end    = 0x0C000FFF,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device sharpsl_nand_device = {
+       .name           = "sharpsl-nand",
+       .id             = -1,
+       .resource       = sharpsl_nand_resources,
+       .num_resources  = ARRAY_SIZE(sharpsl_nand_resources),
+       .dev.platform_data      = &sharpsl_nand_platform_data,
+};
+
 
 static struct mtd_partition sharpsl_rom_parts[] = {
        {
@@ -648,6 +697,7 @@ static struct platform_device *devices[] __initdata = {
        &spitzscoop_device,
        &spitzkbd_device,
        &spitzled_device,
+       &sharpsl_nand_device,
        &sharpsl_rom_device,
 };
 
@@ -671,6 +721,14 @@ static void __init common_init(void)
        pm_power_off = spitz_poweroff;
        arm_pm_restart = spitz_restart;
 
+       if (machine_is_spitz()) {
+               sharpsl_nand_partitions[1].size = 5 * 1024 * 1024;
+       } else if (machine_is_akita()) {
+               sharpsl_nand_partitions[1].size = 58 * 1024 * 1024;
+       } else if (machine_is_borzoi()) {
+               sharpsl_nand_partitions[1].size = 32 * 1024 * 1024;
+       }
+
        PMCR = 0x00;
 
        /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */
@@ -715,10 +773,29 @@ static struct i2c_board_info akita_i2c_board_info[] = {
        },
 };
 
+static struct nand_bbt_descr sharpsl_akita_bbt = {
+       .options = 0,
+       .offs = 4,
+       .len = 1,
+       .pattern = scan_ff_pattern
+};
+
+static struct nand_ecclayout akita_oobinfo = {
+       .eccbytes = 24,
+       .eccpos = {
+                  0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11,
+                  0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
+                  0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37},
+       .oobfree = {{0x08, 0x09}}
+};
+
 static void __init akita_init(void)
 {
        spitz_ficp_platform_data.transceiver_mode = akita_irda_transceiver_mode;
 
+       sharpsl_nand_platform_data.badblock_pattern = &sharpsl_akita_bbt;
+       sharpsl_nand_platform_data.ecc_layout = &akita_oobinfo;
+
        /* We just pretend the second element of the array doesn't exist */
        spitz_pcmcia_config.num_devs = 1;
        platform_scoop_config = &spitz_pcmcia_config;
index a90d50c2c3e5535343e859c5df1c40c3be7e464a..7d04fb9ddcaae15a2ae260c73697d3a99b1106d1 100644 (file)
@@ -45,6 +45,14 @@ config MTD_PARTITIONS
          devices. Partitioning on NFTL 'devices' is a different - that's the
          'normal' form of partitioning used on a block device.
 
+config MTD_TESTS
+       tristate "MTD tests support"
+       depends on m
+       help
+         This option includes various MTD tests into compilation. The tests
+         should normally be compiled as kernel modules. The modules perform
+         various checks and verifications when loaded.
+
 config MTD_REDBOOT_PARTS
        tristate "RedBoot partition table parsing"
        depends on MTD_PARTITIONS
@@ -316,6 +324,8 @@ source "drivers/mtd/nand/Kconfig"
 
 source "drivers/mtd/onenand/Kconfig"
 
+source "drivers/mtd/lpddr/Kconfig"
+
 source "drivers/mtd/ubi/Kconfig"
 
 endif # MTD
index 4b77335715f0cb64bb702178ced7a547e6aaabea..4521b1ecce452607987b3d32c6251264de02d2b8 100644 (file)
@@ -29,6 +29,6 @@ obj-$(CONFIG_MTD_OOPS)                += mtdoops.o
 nftl-objs              := nftlcore.o nftlmount.o
 inftl-objs             := inftlcore.o inftlmount.o
 
-obj-y          += chips/ maps/ devices/ nand/ onenand/
+obj-y          += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
 
 obj-$(CONFIG_MTD_UBI)          += ubi/
index c93a8be5d5f1f541e96c6e104d5d10f4997bf451..f5ab6fa1057bdc422816c8f5e317de466a65107f 100644 (file)
@@ -58,8 +58,8 @@ static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t
 static int cfi_intelext_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *);
 static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
 static void cfi_intelext_sync (struct mtd_info *);
-static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
-static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
+static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 #ifdef CONFIG_MTD_OTP
 static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
@@ -558,8 +558,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
        }
 
        for (i=0; i<mtd->numeraseregions;i++){
-               printk(KERN_DEBUG "erase region %d: offset=0x%x,size=0x%x,blocks=%d\n",
-                      i,mtd->eraseregions[i].offset,
+               printk(KERN_DEBUG "erase region %d: offset=0x%llx,size=0x%x,blocks=%d\n",
+                      i,(unsigned long long)mtd->eraseregions[i].offset,
                       mtd->eraseregions[i].erasesize,
                       mtd->eraseregions[i].numblocks);
        }
@@ -2058,7 +2058,7 @@ out:      put_chip(map, chip, adr);
        return ret;
 }
 
-static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        int ret;
 
@@ -2082,7 +2082,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
        return ret;
 }
 
-static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        int ret;
 
index d74ec46aa032220f3159739667b6f039620f77a8..94bb61e19047764ae06dcf91408946219869fa06 100644 (file)
@@ -71,8 +71,8 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
 static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
 #include "fwh_lock.h"
 
-static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
-static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
+static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 
 static struct mtd_chip_driver cfi_amdstd_chipdrv = {
        .probe          = NULL, /* Not usable directly */
@@ -322,6 +322,14 @@ static struct cfi_fixup fixup_table[] = {
 };
 
 
+static void cfi_fixup_major_minor(struct cfi_private *cfi,
+                                 struct cfi_pri_amdstd *extp)
+{
+       if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e &&
+           extp->MajorVersion == '0')
+               extp->MajorVersion = '1';
+}
+
 struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 {
        struct cfi_private *cfi = map->fldrv_priv;
@@ -363,6 +371,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
                        return NULL;
                }
 
+               cfi_fixup_major_minor(cfi, extp);
+
                if (extp->MajorVersion != '1' ||
                    (extp->MinorVersion < '0' || extp->MinorVersion > '4')) {
                        printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
@@ -1774,12 +1784,12 @@ out_unlock:
        return ret;
 }
 
-static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        return cfi_varsize_frob(mtd, do_atmel_lock, ofs, len, NULL);
 }
 
-static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL);
 }
index d4714dd9f7ab0d1f6626f87b584899844bc056e0..6c740f346f910e50ddf4a8fba8329e13498cb13b 100644 (file)
@@ -42,8 +42,8 @@ static int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs,
                unsigned long count, loff_t to, size_t *retlen);
 static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *);
 static void cfi_staa_sync (struct mtd_info *);
-static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
-static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
+static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 static int cfi_staa_suspend (struct mtd_info *);
 static void cfi_staa_resume (struct mtd_info *);
 
@@ -221,8 +221,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
                }
 
                for (i=0; i<mtd->numeraseregions;i++){
-                       printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n",
-                              i,mtd->eraseregions[i].offset,
+                       printk(KERN_DEBUG "%d: offset=0x%llx,size=0x%x,blocks=%d\n",
+                              i, (unsigned long long)mtd->eraseregions[i].offset,
                               mtd->eraseregions[i].erasesize,
                               mtd->eraseregions[i].numblocks);
                }
@@ -964,7 +964,7 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,
                adr += regions[i].erasesize;
                len -= regions[i].erasesize;
 
-               if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
+               if (adr % (1<< cfi->chipshift) == (((unsigned long)regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
                        i++;
 
                if (adr >> cfi->chipshift) {
@@ -1135,7 +1135,7 @@ retry:
        spin_unlock_bh(chip->mutex);
        return 0;
 }
-static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct map_info *map = mtd->priv;
        struct cfi_private *cfi = map->fldrv_priv;
@@ -1284,7 +1284,7 @@ retry:
        spin_unlock_bh(chip->mutex);
        return 0;
 }
-static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct map_info *map = mtd->priv;
        struct cfi_private *cfi = map->fldrv_priv;
index ab44f2b996f8ceb4f2729fa8fdc7c1956386a1d6..57e0e4e921f9505f2fd12e30cd5d837dc1721f57 100644 (file)
@@ -77,7 +77,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
 }
 
 
-static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        int ret;
 
@@ -88,7 +88,7 @@ static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
 }
 
 
-static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        int ret;
 
index f4bda4cee4958869d12291bfa93ad10939eedeb4..578de1c67bfe047a78b43c95845fdc6d5aae43e3 100644 (file)
@@ -619,7 +619,7 @@ static struct mtd_partition lart_partitions[] = {
 };
 #endif
 
-int __init lart_flash_init (void)
+static int __init lart_flash_init (void)
 {
    int result;
    memset (&mtd,0,sizeof (mtd));
@@ -690,7 +690,7 @@ int __init lart_flash_init (void)
    return (result);
 }
 
-void __exit lart_flash_exit (void)
+static void __exit lart_flash_exit (void)
 {
 #ifndef HAVE_PARTITIONS
    del_mtd_device (&mtd);
@@ -705,5 +705,3 @@ module_exit (lart_flash_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Abraham vd Merwe <abraham@2d3d.co.za>");
 MODULE_DESCRIPTION("MTD driver for Intel 28F160F3 on LART board");
-
-
index 5733f0643843a78e129e0c83a0ba6c9bd368ff7b..7c3fc766dcf17b4b6cfcd2dec41d7220a75a03be 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
+#include <linux/math64.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -169,9 +170,9 @@ static int wait_till_ready(struct m25p *flash)
  */
 static int erase_chip(struct m25p *flash)
 {
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB\n",
-                       dev_name(&flash->spi->dev), __func__,
-                       flash->mtd.size / 1024);
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n",
+             dev_name(&flash->spi->dev), __func__,
+             (long long)(flash->mtd.size >> 10));
 
        /* Wait until finished previous write command. */
        if (wait_till_ready(flash))
@@ -232,18 +233,18 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct m25p *flash = mtd_to_m25p(mtd);
        u32 addr,len;
+       uint32_t rem;
 
-       DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n",
-                       dev_name(&flash->spi->dev), __func__, "at",
-                       (u32)instr->addr, instr->len);
+       DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n",
+             dev_name(&flash->spi->dev), __func__, "at",
+             (long long)instr->addr, (long long)instr->len);
 
        /* sanity checks */
        if (instr->addr + instr->len > flash->mtd.size)
                return -EINVAL;
-       if ((instr->addr % mtd->erasesize) != 0
-                       || (instr->len % mtd->erasesize) != 0) {
+       div_u64_rem(instr->len, mtd->erasesize, &rem);
+       if (rem)
                return -EINVAL;
-       }
 
        addr = instr->addr;
        len = instr->len;
@@ -677,24 +678,24 @@ static int __devinit m25p_probe(struct spi_device *spi)
                flash->mtd.erasesize = info->sector_size;
        }
 
-       dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name,
-                       flash->mtd.size / 1024);
+       dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name,
+                       (long long)flash->mtd.size >> 10);
 
        DEBUG(MTD_DEBUG_LEVEL2,
-               "mtd .name = %s, .size = 0x%.8x (%uMiB) "
+               "mtd .name = %s, .size = 0x%llx (%lldMiB) "
                        ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
                flash->mtd.name,
-               flash->mtd.size, flash->mtd.size / (1024*1024),
+               (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
                flash->mtd.erasesize, flash->mtd.erasesize / 1024,
                flash->mtd.numeraseregions);
 
        if (flash->mtd.numeraseregions)
                for (i = 0; i < flash->mtd.numeraseregions; i++)
                        DEBUG(MTD_DEBUG_LEVEL2,
-                               "mtd.eraseregions[%d] = { .offset = 0x%.8x, "
+                               "mtd.eraseregions[%d] = { .offset = 0x%llx, "
                                ".erasesize = 0x%.8x (%uKiB), "
                                ".numblocks = %d }\n",
-                               i, flash->mtd.eraseregions[i].offset,
+                               i, (long long)flash->mtd.eraseregions[i].offset,
                                flash->mtd.eraseregions[i].erasesize,
                                flash->mtd.eraseregions[i].erasesize / 1024,
                                flash->mtd.eraseregions[i].numblocks);
@@ -722,12 +723,12 @@ static int __devinit m25p_probe(struct spi_device *spi)
                if (nr_parts > 0) {
                        for (i = 0; i < nr_parts; i++) {
                                DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
-                                       "{.name = %s, .offset = 0x%.8x, "
-                                               ".size = 0x%.8x (%uKiB) }\n",
+                                       "{.name = %s, .offset = 0x%llx, "
+                                               ".size = 0x%llx (%lldKiB) }\n",
                                        i, parts[i].name,
-                                       parts[i].offset,
-                                       parts[i].size,
-                                       parts[i].size / 1024);
+                                       (long long)parts[i].offset,
+                                       (long long)parts[i].size,
+                                       (long long)(parts[i].size >> 10));
                        }
                        flash->partitioned = 1;
                        return add_mtd_partitions(&flash->mtd, parts, nr_parts);
index 65126cd668ff3fbdeaf1437a4a1aeb7168ad4cd5..d44f741ae229cc89f342311e7969335a3cc527ca 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/device.h>
 #include <linux/mutex.h>
 #include <linux/err.h>
+#include <linux/math64.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
@@ -152,15 +153,20 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
        struct spi_message      msg;
        unsigned                blocksize = priv->page_size << 3;
        uint8_t                 *command;
+       uint32_t                rem;
 
-       DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n",
-                       dev_name(&spi->dev),
-                       instr->addr, instr->len);
+       DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%llx len 0x%llx\n",
+             dev_name(&spi->dev), (long long)instr->addr,
+             (long long)instr->len);
 
        /* Sanity checks */
-       if ((instr->addr + instr->len) > mtd->size
-                       || (instr->len % priv->page_size) != 0
-                       || (instr->addr % priv->page_size) != 0)
+       if (instr->addr + instr->len > mtd->size)
+               return -EINVAL;
+       div_u64_rem(instr->len, priv->page_size, &rem);
+       if (rem)
+               return -EINVAL;
+       div_u64_rem(instr->addr, priv->page_size, &rem);
+       if (rem)
                return -EINVAL;
 
        spi_message_init(&msg);
@@ -178,7 +184,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
                /* Calculate flash page address; use block erase (for speed) if
                 * we're at a block boundary and need to erase the whole block.
                 */
-               pageaddr = instr->addr / priv->page_size;
+               pageaddr = div_u64(instr->len, priv->page_size);
                do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize;
                pageaddr = pageaddr << priv->page_offset;
 
@@ -667,8 +673,8 @@ add_dataflash_otp(struct spi_device *spi, char *name,
        if (revision >= 'c')
                otp_tag = otp_setup(device, revision);
 
-       dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes%s\n",
-                       name, DIV_ROUND_UP(device->size, 1024),
+       dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n",
+                       name, (long long)((device->size + 1023) >> 10),
                        pagesize, otp_tag);
        dev_set_drvdata(&spi->dev, priv);
 
index 9bf581c4f740e775f590842004fc52568ce8fc9e..a790c062af1f2688a9f568fec8b62b8194e8d724 100644 (file)
@@ -109,25 +109,25 @@ module_param(shuffle_freq, int, 0);
 /* Each memory region corresponds to a minor device */
 typedef struct partition_t {
     struct mtd_blktrans_dev mbd;
-    u_int32_t          state;
-    u_int32_t          *VirtualBlockMap;
-    u_int32_t          *VirtualPageMap;
-    u_int32_t          FreeTotal;
+    uint32_t           state;
+    uint32_t           *VirtualBlockMap;
+    uint32_t           *VirtualPageMap;
+    uint32_t           FreeTotal;
     struct eun_info_t {
-       u_int32_t               Offset;
-       u_int32_t               EraseCount;
-       u_int32_t               Free;
-       u_int32_t               Deleted;
+       uint32_t                Offset;
+       uint32_t                EraseCount;
+       uint32_t                Free;
+       uint32_t                Deleted;
     } *EUNInfo;
     struct xfer_info_t {
-       u_int32_t               Offset;
-       u_int32_t               EraseCount;
-       u_int16_t               state;
+       uint32_t                Offset;
+       uint32_t                EraseCount;
+       uint16_t                state;
     } *XferInfo;
-    u_int16_t          bam_index;
-    u_int32_t          *bam_cache;
-    u_int16_t          DataUnits;
-    u_int32_t          BlocksPerUnit;
+    uint16_t           bam_index;
+    uint32_t           *bam_cache;
+    uint16_t           DataUnits;
+    uint32_t           BlocksPerUnit;
     erase_unit_header_t        header;
 } partition_t;
 
@@ -199,8 +199,8 @@ static int scan_header(partition_t *part)
 static int build_maps(partition_t *part)
 {
     erase_unit_header_t header;
-    u_int16_t xvalid, xtrans, i;
-    u_int blocks, j;
+    uint16_t xvalid, xtrans, i;
+    unsigned blocks, j;
     int hdr_ok, ret = -1;
     ssize_t retval;
     loff_t offset;
@@ -269,14 +269,14 @@ static int build_maps(partition_t *part)
 
     /* Set up virtual page map */
     blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
-    part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t));
+    part->VirtualBlockMap = vmalloc(blocks * sizeof(uint32_t));
     if (!part->VirtualBlockMap)
            goto out_XferInfo;
 
-    memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t));
+    memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
     part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
 
-    part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t),
+    part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(uint32_t),
                              GFP_KERNEL);
     if (!part->bam_cache)
            goto out_VirtualBlockMap;
@@ -290,7 +290,7 @@ static int build_maps(partition_t *part)
        offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
 
        ret = part->mbd.mtd->read(part->mbd.mtd, offset,
-                             part->BlocksPerUnit * sizeof(u_int32_t), &retval,
+                             part->BlocksPerUnit * sizeof(uint32_t), &retval,
                              (unsigned char *)part->bam_cache);
 
        if (ret)
@@ -332,7 +332,7 @@ out:
 ======================================================================*/
 
 static int erase_xfer(partition_t *part,
-                     u_int16_t xfernum)
+                     uint16_t xfernum)
 {
     int ret;
     struct xfer_info_t *xfer;
@@ -408,7 +408,7 @@ static int prepare_xfer(partition_t *part, int i)
     erase_unit_header_t header;
     struct xfer_info_t *xfer;
     int nbam, ret;
-    u_int32_t ctl;
+    uint32_t ctl;
     ssize_t retlen;
     loff_t offset;
 
@@ -430,15 +430,15 @@ static int prepare_xfer(partition_t *part, int i)
     }
 
     /* Write the BAM stub */
-    nbam = (part->BlocksPerUnit * sizeof(u_int32_t) +
+    nbam = (part->BlocksPerUnit * sizeof(uint32_t) +
            le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
 
     offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
     ctl = cpu_to_le32(BLOCK_CONTROL);
 
-    for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
+    for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
 
-       ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
+       ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint32_t),
                               &retlen, (u_char *)&ctl);
 
        if (ret)
@@ -461,18 +461,18 @@ static int prepare_xfer(partition_t *part, int i)
 
 ======================================================================*/
 
-static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
-                          u_int16_t xferunit)
+static int copy_erase_unit(partition_t *part, uint16_t srcunit,
+                          uint16_t xferunit)
 {
     u_char buf[SECTOR_SIZE];
     struct eun_info_t *eun;
     struct xfer_info_t *xfer;
-    u_int32_t src, dest, free, i;
-    u_int16_t unit;
+    uint32_t src, dest, free, i;
+    uint16_t unit;
     int ret;
     ssize_t retlen;
     loff_t offset;
-    u_int16_t srcunitswap = cpu_to_le16(srcunit);
+    uint16_t srcunitswap = cpu_to_le16(srcunit);
 
     eun = &part->EUNInfo[srcunit];
     xfer = &part->XferInfo[xferunit];
@@ -486,7 +486,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
        offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
 
        ret = part->mbd.mtd->read(part->mbd.mtd, offset,
-                             part->BlocksPerUnit * sizeof(u_int32_t),
+                             part->BlocksPerUnit * sizeof(uint32_t),
                              &retlen, (u_char *) (part->bam_cache));
 
        /* mark the cache bad, in case we get an error later */
@@ -503,7 +503,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
     offset = xfer->Offset + 20; /* Bad! */
     unit = cpu_to_le16(0x7fff);
 
-    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t),
+    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint16_t),
                           &retlen, (u_char *) &unit);
 
     if (ret) {
@@ -560,7 +560,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
 
 
     /* All clear? Then update the LogicalEUN again */
-    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t),
+    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
                           &retlen, (u_char *)&srcunitswap);
 
     if (ret) {
@@ -605,8 +605,8 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
 
 static int reclaim_block(partition_t *part)
 {
-    u_int16_t i, eun, xfer;
-    u_int32_t best;
+    uint16_t i, eun, xfer;
+    uint32_t best;
     int queued, ret;
 
     DEBUG(0, "ftl_cs: reclaiming space...\n");
@@ -723,10 +723,10 @@ static void dump_lists(partition_t *part)
 }
 #endif
 
-static u_int32_t find_free(partition_t *part)
+static uint32_t find_free(partition_t *part)
 {
-    u_int16_t stop, eun;
-    u_int32_t blk;
+    uint16_t stop, eun;
+    uint32_t blk;
     size_t retlen;
     int ret;
 
@@ -749,7 +749,7 @@ static u_int32_t find_free(partition_t *part)
 
        ret = part->mbd.mtd->read(part->mbd.mtd,
                       part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
-                      part->BlocksPerUnit * sizeof(u_int32_t),
+                      part->BlocksPerUnit * sizeof(uint32_t),
                       &retlen, (u_char *) (part->bam_cache));
 
        if (ret) {
@@ -786,7 +786,7 @@ static u_int32_t find_free(partition_t *part)
 static int ftl_read(partition_t *part, caddr_t buffer,
                    u_long sector, u_long nblocks)
 {
-    u_int32_t log_addr, bsize;
+    uint32_t log_addr, bsize;
     u_long i;
     int ret;
     size_t offset, retlen;
@@ -829,14 +829,14 @@ static int ftl_read(partition_t *part, caddr_t buffer,
 
 ======================================================================*/
 
-static int set_bam_entry(partition_t *part, u_int32_t log_addr,
-                        u_int32_t virt_addr)
+static int set_bam_entry(partition_t *part, uint32_t log_addr,
+                        uint32_t virt_addr)
 {
-    u_int32_t bsize, blk, le_virt_addr;
+    uint32_t bsize, blk, le_virt_addr;
 #ifdef PSYCHO_DEBUG
-    u_int32_t old_addr;
+    uint32_t old_addr;
 #endif
-    u_int16_t eun;
+    uint16_t eun;
     int ret;
     size_t retlen, offset;
 
@@ -845,11 +845,11 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr,
     bsize = 1 << part->header.EraseUnitSize;
     eun = log_addr / bsize;
     blk = (log_addr % bsize) / SECTOR_SIZE;
-    offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) +
+    offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
                  le32_to_cpu(part->header.BAMOffset));
 
 #ifdef PSYCHO_DEBUG
-    ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t),
+    ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(uint32_t),
                         &retlen, (u_char *)&old_addr);
     if (ret) {
        printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
@@ -886,7 +886,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr,
 #endif
        part->bam_cache[blk] = le_virt_addr;
     }
-    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
+    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint32_t),
                             &retlen, (u_char *)&le_virt_addr);
 
     if (ret) {
@@ -900,7 +900,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr,
 static int ftl_write(partition_t *part, caddr_t buffer,
                     u_long sector, u_long nblocks)
 {
-    u_int32_t bsize, log_addr, virt_addr, old_addr, blk;
+    uint32_t bsize, log_addr, virt_addr, old_addr, blk;
     u_long i;
     int ret;
     size_t retlen, offset;
index 50ce13887f63b6b81d2525a5070fbfc511643b1c..73f05227dc8cb0607d11c6709a2233959f65d92a 100644 (file)
@@ -50,7 +50,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        struct INFTLrecord *inftl;
        unsigned long temp;
 
-       if (mtd->type != MTD_NANDFLASH)
+       if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX)
                return;
        /* OK, this is moderately ugly.  But probably safe.  Alternatives? */
        if (memcmp(mtd->name, "DiskOnChip", 10))
index 9113628ed1ef3b54505780efcd7dc729537ea202..f751dd97c5493a1a8e732e8745c759fad56f6cfa 100644 (file)
@@ -63,7 +63,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
         * otherwise.
         */
        inftl->EraseSize = inftl->mbd.mtd->erasesize;
-        inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
+        inftl->nb_blocks = (u32)inftl->mbd.mtd->size / inftl->EraseSize;
 
        inftl->MediaUnit = BLOCK_NIL;
 
@@ -187,7 +187,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
                                mh->BlockMultiplierBits);
                        inftl->EraseSize = inftl->mbd.mtd->erasesize <<
                                mh->BlockMultiplierBits;
-                       inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
+                       inftl->nb_blocks = (u32)inftl->mbd.mtd->size / inftl->EraseSize;
                        block >>= mh->BlockMultiplierBits;
                }
 
diff --git a/drivers/mtd/lpddr/Kconfig b/drivers/mtd/lpddr/Kconfig
new file mode 100644 (file)
index 0000000..acd4ea9
--- /dev/null
@@ -0,0 +1,22 @@
+# drivers/mtd/chips/Kconfig
+
+menu "LPDDR flash memory drivers"
+       depends on MTD!=n
+
+config MTD_LPDDR
+       tristate "Support for LPDDR flash chips"
+       select MTD_QINFO_PROBE
+       help
+         This option enables support of LPDDR (Low power double data rate)
+         flash chips. Synonymous with Mobile-DDR. It is a new standard for
+         DDR memories, intended for battery-operated systems.
+
+config MTD_QINFO_PROBE
+       tristate "Detect flash chips by QINFO probe"
+       help
+           Device Information for LPDDR chips is offered through the Overlay
+           Window QINFO interface, permits software to be used for entire
+           families of devices. This serves similar purpose of CFI on legacy
+           Flash products
+endmenu
+
diff --git a/drivers/mtd/lpddr/Makefile b/drivers/mtd/lpddr/Makefile
new file mode 100644 (file)
index 0000000..da48e46
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# linux/drivers/mtd/lpddr/Makefile
+#
+
+obj-$(CONFIG_MTD_QINFO_PROBE)  += qinfo_probe.o
+obj-$(CONFIG_MTD_LPDDR)        += lpddr_cmds.o
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
new file mode 100644 (file)
index 0000000..e22ca49
--- /dev/null
@@ -0,0 +1,796 @@
+/*
+ * LPDDR flash memory device operations. This module provides read, write,
+ * erase, lock/unlock support for LPDDR flash memories
+ * (C) 2008 Korolev Alexey <akorolev@infradead.org>
+ * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com>
+ * Many thanks to Roman Borisov for intial enabling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ * TODO:
+ * Implement VPP management
+ * Implement XIP support
+ * Implement OTP support
+ */
+#include <linux/mtd/pfow.h>
+#include <linux/mtd/qinfo.h>
+
+static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len,
+                                       size_t *retlen, u_char *buf);
+static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to,
+                               size_t len, size_t *retlen, const u_char *buf);
+static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs,
+                               unsigned long count, loff_t to, size_t *retlen);
+static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr);
+static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
+                       size_t *retlen, void **mtdbuf, resource_size_t *phys);
+static void lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len);
+static int get_chip(struct map_info *map, struct flchip *chip, int mode);
+static int chip_ready(struct map_info *map, struct flchip *chip, int mode);
+static void put_chip(struct map_info *map, struct flchip *chip);
+
+struct mtd_info *lpddr_cmdset(struct map_info *map)
+{
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       struct flchip_shared *shared;
+       struct flchip *chip;
+       struct mtd_info *mtd;
+       int numchips;
+       int i, j;
+
+       mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
+       if (!mtd) {
+               printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+               return NULL;
+       }
+       mtd->priv = map;
+       mtd->type = MTD_NORFLASH;
+
+       /* Fill in the default mtd operations */
+       mtd->read = lpddr_read;
+       mtd->type = MTD_NORFLASH;
+       mtd->flags = MTD_CAP_NORFLASH;
+       mtd->flags &= ~MTD_BIT_WRITEABLE;
+       mtd->erase = lpddr_erase;
+       mtd->write = lpddr_write_buffers;
+       mtd->writev = lpddr_writev;
+       mtd->read_oob = NULL;
+       mtd->write_oob = NULL;
+       mtd->sync = NULL;
+       mtd->lock = lpddr_lock;
+       mtd->unlock = lpddr_unlock;
+       mtd->suspend = NULL;
+       mtd->resume = NULL;
+       if (map_is_linear(map)) {
+               mtd->point = lpddr_point;
+               mtd->unpoint = lpddr_unpoint;
+       }
+       mtd->block_isbad = NULL;
+       mtd->block_markbad = NULL;
+       mtd->size = 1 << lpddr->qinfo->DevSizeShift;
+       mtd->erasesize = 1 << lpddr->qinfo->UniformBlockSizeShift;
+       mtd->writesize = 1 << lpddr->qinfo->BufSizeShift;
+
+       shared = kmalloc(sizeof(struct flchip_shared) * lpddr->numchips,
+                                               GFP_KERNEL);
+       if (!shared) {
+               kfree(lpddr);
+               kfree(mtd);
+               return NULL;
+       }
+
+       chip = &lpddr->chips[0];
+       numchips = lpddr->numchips / lpddr->qinfo->HWPartsNum;
+       for (i = 0; i < numchips; i++) {
+               shared[i].writing = shared[i].erasing = NULL;
+               spin_lock_init(&shared[i].lock);
+               for (j = 0; j < lpddr->qinfo->HWPartsNum; j++) {
+                       *chip = lpddr->chips[i];
+                       chip->start += j << lpddr->chipshift;
+                       chip->oldstate = chip->state = FL_READY;
+                       chip->priv = &shared[i];
+                       /* those should be reset too since
+                          they create memory references. */
+                       init_waitqueue_head(&chip->wq);
+                       spin_lock_init(&chip->_spinlock);
+                       chip->mutex = &chip->_spinlock;
+                       chip++;
+               }
+       }
+
+       return mtd;
+}
+EXPORT_SYMBOL(lpddr_cmdset);
+
+static int wait_for_ready(struct map_info *map, struct flchip *chip,
+               unsigned int chip_op_time)
+{
+       unsigned int timeo, reset_timeo, sleep_time;
+       unsigned int dsr;
+       flstate_t chip_state = chip->state;
+       int ret = 0;
+
+       /* set our timeout to 8 times the expected delay */
+       timeo = chip_op_time * 8;
+       if (!timeo)
+               timeo = 500000;
+       reset_timeo = timeo;
+       sleep_time = chip_op_time / 2;
+
+       for (;;) {
+               dsr = CMDVAL(map_read(map, map->pfow_base + PFOW_DSR));
+               if (dsr & DSR_READY_STATUS)
+                       break;
+               if (!timeo) {
+                       printk(KERN_ERR "%s: Flash timeout error state %d \n",
+                                                       map->name, chip_state);
+                       ret = -ETIME;
+                       break;
+               }
+
+               /* OK Still waiting. Drop the lock, wait a while and retry. */
+               spin_unlock(chip->mutex);
+               if (sleep_time >= 1000000/HZ) {
+                       /*
+                        * Half of the normal delay still remaining
+                        * can be performed with a sleeping delay instead
+                        * of busy waiting.
+                        */
+                       msleep(sleep_time/1000);
+                       timeo -= sleep_time;
+                       sleep_time = 1000000/HZ;
+               } else {
+                       udelay(1);
+                       cond_resched();
+                       timeo--;
+               }
+               spin_lock(chip->mutex);
+
+               while (chip->state != chip_state) {
+                       /* Someone's suspended the operation: sleep */
+                       DECLARE_WAITQUEUE(wait, current);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       add_wait_queue(&chip->wq, &wait);
+                       spin_unlock(chip->mutex);
+                       schedule();
+                       remove_wait_queue(&chip->wq, &wait);
+                       spin_lock(chip->mutex);
+               }
+               if (chip->erase_suspended || chip->write_suspended)  {
+                       /* Suspend has occured while sleep: reset timeout */
+                       timeo = reset_timeo;
+                       chip->erase_suspended = chip->write_suspended = 0;
+               }
+       }
+       /* check status for errors */
+       if (dsr & DSR_ERR) {
+               /* Clear DSR*/
+               map_write(map, CMD(~(DSR_ERR)), map->pfow_base + PFOW_DSR);
+               printk(KERN_WARNING"%s: Bad status on wait: 0x%x \n",
+                               map->name, dsr);
+               print_drs_error(dsr);
+               ret = -EIO;
+       }
+       chip->state = FL_READY;
+       return ret;
+}
+
+static int get_chip(struct map_info *map, struct flchip *chip, int mode)
+{
+       int ret;
+       DECLARE_WAITQUEUE(wait, current);
+
+ retry:
+       if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)
+               && chip->state != FL_SYNCING) {
+               /*
+                * OK. We have possibility for contension on the write/erase
+                * operations which are global to the real chip and not per
+                * partition.  So let's fight it over in the partition which
+                * currently has authority on the operation.
+                *
+                * The rules are as follows:
+                *
+                * - any write operation must own shared->writing.
+                *
+                * - any erase operation must own _both_ shared->writing and
+                *   shared->erasing.
+                *
+                * - contension arbitration is handled in the owner's context.
+                *
+                * The 'shared' struct can be read and/or written only when
+                * its lock is taken.
+                */
+               struct flchip_shared *shared = chip->priv;
+               struct flchip *contender;
+               spin_lock(&shared->lock);
+               contender = shared->writing;
+               if (contender && contender != chip) {
+                       /*
+                        * The engine to perform desired operation on this
+                        * partition is already in use by someone else.
+                        * Let's fight over it in the context of the chip
+                        * currently using it.  If it is possible to suspend,
+                        * that other partition will do just that, otherwise
+                        * it'll happily send us to sleep.  In any case, when
+                        * get_chip returns success we're clear to go ahead.
+                        */
+                       ret = spin_trylock(contender->mutex);
+                       spin_unlock(&shared->lock);
+                       if (!ret)
+                               goto retry;
+                       spin_unlock(chip->mutex);
+                       ret = chip_ready(map, contender, mode);
+                       spin_lock(chip->mutex);
+
+                       if (ret == -EAGAIN) {
+                               spin_unlock(contender->mutex);
+                               goto retry;
+                       }
+                       if (ret) {
+                               spin_unlock(contender->mutex);
+                               return ret;
+                       }
+                       spin_lock(&shared->lock);
+
+                       /* We should not own chip if it is already in FL_SYNCING
+                        * state. Put contender and retry. */
+                       if (chip->state == FL_SYNCING) {
+                               put_chip(map, contender);
+                               spin_unlock(contender->mutex);
+                               goto retry;
+                       }
+                       spin_unlock(contender->mutex);
+               }
+
+               /* Check if we have suspended erase on this chip.
+                  Must sleep in such a case. */
+               if (mode == FL_ERASING && shared->erasing
+                   && shared->erasing->oldstate == FL_ERASING) {
+                       spin_unlock(&shared->lock);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       add_wait_queue(&chip->wq, &wait);
+                       spin_unlock(chip->mutex);
+                       schedule();
+                       remove_wait_queue(&chip->wq, &wait);
+                       spin_lock(chip->mutex);
+                       goto retry;
+               }
+
+               /* We now own it */
+               shared->writing = chip;
+               if (mode == FL_ERASING)
+                       shared->erasing = chip;
+               spin_unlock(&shared->lock);
+       }
+
+       ret = chip_ready(map, chip, mode);
+       if (ret == -EAGAIN)
+               goto retry;
+
+       return ret;
+}
+
+static int chip_ready(struct map_info *map, struct flchip *chip, int mode)
+{
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int ret = 0;
+       DECLARE_WAITQUEUE(wait, current);
+
+       /* Prevent setting state FL_SYNCING for chip in suspended state. */
+       if (FL_SYNCING == mode && FL_READY != chip->oldstate)
+               goto sleep;
+
+       switch (chip->state) {
+       case FL_READY:
+       case FL_JEDEC_QUERY:
+               return 0;
+
+       case FL_ERASING:
+               if (!lpddr->qinfo->SuspEraseSupp ||
+                       !(mode == FL_READY || mode == FL_POINT))
+                       goto sleep;
+
+               map_write(map, CMD(LPDDR_SUSPEND),
+                       map->pfow_base + PFOW_PROGRAM_ERASE_SUSPEND);
+               chip->oldstate = FL_ERASING;
+               chip->state = FL_ERASE_SUSPENDING;
+               ret = wait_for_ready(map, chip, 0);
+               if (ret) {
+                       /* Oops. something got wrong. */
+                       /* Resume and pretend we weren't here.  */
+                       map_write(map, CMD(LPDDR_RESUME),
+                               map->pfow_base + PFOW_COMMAND_CODE);
+                       map_write(map, CMD(LPDDR_START_EXECUTION),
+                               map->pfow_base + PFOW_COMMAND_EXECUTE);
+                       chip->state = FL_ERASING;
+                       chip->oldstate = FL_READY;
+                       printk(KERN_ERR "%s: suspend operation failed."
+                                       "State may be wrong \n", map->name);
+                       return -EIO;
+               }
+               chip->erase_suspended = 1;
+               chip->state = FL_READY;
+               return 0;
+               /* Erase suspend */
+       case FL_POINT:
+               /* Only if there's no operation suspended... */
+               if (mode == FL_READY && chip->oldstate == FL_READY)
+                       return 0;
+
+       default:
+sleep:
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               add_wait_queue(&chip->wq, &wait);
+               spin_unlock(chip->mutex);
+               schedule();
+               remove_wait_queue(&chip->wq, &wait);
+               spin_lock(chip->mutex);
+               return -EAGAIN;
+       }
+}
+
+static void put_chip(struct map_info *map, struct flchip *chip)
+{
+       if (chip->priv) {
+               struct flchip_shared *shared = chip->priv;
+               spin_lock(&shared->lock);
+               if (shared->writing == chip && chip->oldstate == FL_READY) {
+                       /* We own the ability to write, but we're done */
+                       shared->writing = shared->erasing;
+                       if (shared->writing && shared->writing != chip) {
+                               /* give back the ownership */
+                               struct flchip *loaner = shared->writing;
+                               spin_lock(loaner->mutex);
+                               spin_unlock(&shared->lock);
+                               spin_unlock(chip->mutex);
+                               put_chip(map, loaner);
+                               spin_lock(chip->mutex);
+                               spin_unlock(loaner->mutex);
+                               wake_up(&chip->wq);
+                               return;
+                       }
+                       shared->erasing = NULL;
+                       shared->writing = NULL;
+               } else if (shared->erasing == chip && shared->writing != chip) {
+                       /*
+                        * We own the ability to erase without the ability
+                        * to write, which means the erase was suspended
+                        * and some other partition is currently writing.
+                        * Don't let the switch below mess things up since
+                        * we don't have ownership to resume anything.
+                        */
+                       spin_unlock(&shared->lock);
+                       wake_up(&chip->wq);
+                       return;
+               }
+               spin_unlock(&shared->lock);
+       }
+
+       switch (chip->oldstate) {
+       case FL_ERASING:
+               chip->state = chip->oldstate;
+               map_write(map, CMD(LPDDR_RESUME),
+                               map->pfow_base + PFOW_COMMAND_CODE);
+               map_write(map, CMD(LPDDR_START_EXECUTION),
+                               map->pfow_base + PFOW_COMMAND_EXECUTE);
+               chip->oldstate = FL_READY;
+               chip->state = FL_ERASING;
+               break;
+       case FL_READY:
+               break;
+       default:
+               printk(KERN_ERR "%s: put_chip() called with oldstate %d!\n",
+                               map->name, chip->oldstate);
+       }
+       wake_up(&chip->wq);
+}
+
+int do_write_buffer(struct map_info *map, struct flchip *chip,
+                       unsigned long adr, const struct kvec **pvec,
+                       unsigned long *pvec_seek, int len)
+{
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       map_word datum;
+       int ret, wbufsize, word_gap, words;
+       const struct kvec *vec;
+       unsigned long vec_seek;
+       unsigned long prog_buf_ofs;
+
+       wbufsize = 1 << lpddr->qinfo->BufSizeShift;
+
+       spin_lock(chip->mutex);
+       ret = get_chip(map, chip, FL_WRITING);
+       if (ret) {
+               spin_unlock(chip->mutex);
+               return ret;
+       }
+       /* Figure out the number of words to write */
+       word_gap = (-adr & (map_bankwidth(map)-1));
+       words = (len - word_gap + map_bankwidth(map) - 1) / map_bankwidth(map);
+       if (!word_gap) {
+               words--;
+       } else {
+               word_gap = map_bankwidth(map) - word_gap;
+               adr -= word_gap;
+               datum = map_word_ff(map);
+       }
+       /* Write data */
+       /* Get the program buffer offset from PFOW register data first*/
+       prog_buf_ofs = map->pfow_base + CMDVAL(map_read(map,
+                               map->pfow_base + PFOW_PROGRAM_BUFFER_OFFSET));
+       vec = *pvec;
+       vec_seek = *pvec_seek;
+       do {
+               int n = map_bankwidth(map) - word_gap;
+
+               if (n > vec->iov_len - vec_seek)
+                       n = vec->iov_len - vec_seek;
+               if (n > len)
+                       n = len;
+
+               if (!word_gap && (len < map_bankwidth(map)))
+                       datum = map_word_ff(map);
+
+               datum = map_word_load_partial(map, datum,
+                               vec->iov_base + vec_seek, word_gap, n);
+
+               len -= n;
+               word_gap += n;
+               if (!len || word_gap == map_bankwidth(map)) {
+                       map_write(map, datum, prog_buf_ofs);
+                       prog_buf_ofs += map_bankwidth(map);
+                       word_gap = 0;
+               }
+
+               vec_seek += n;
+               if (vec_seek == vec->iov_len) {
+                       vec++;
+                       vec_seek = 0;
+               }
+       } while (len);
+       *pvec = vec;
+       *pvec_seek = vec_seek;
+
+       /* GO GO GO */
+       send_pfow_command(map, LPDDR_BUFF_PROGRAM, adr, wbufsize, NULL);
+       chip->state = FL_WRITING;
+       ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->ProgBufferTime));
+       if (ret)        {
+               printk(KERN_WARNING"%s Buffer program error: %d at %lx; \n",
+                       map->name, ret, adr);
+               goto out;
+       }
+
+ out:  put_chip(map, chip);
+       spin_unlock(chip->mutex);
+       return ret;
+}
+
+int do_erase_oneblock(struct mtd_info *mtd, loff_t adr)
+{
+       struct map_info *map = mtd->priv;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int chipnum = adr >> lpddr->chipshift;
+       struct flchip *chip = &lpddr->chips[chipnum];
+       int ret;
+
+       spin_lock(chip->mutex);
+       ret = get_chip(map, chip, FL_ERASING);
+       if (ret) {
+               spin_unlock(chip->mutex);
+               return ret;
+       }
+       send_pfow_command(map, LPDDR_BLOCK_ERASE, adr, 0, NULL);
+       chip->state = FL_ERASING;
+       ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->BlockEraseTime)*1000);
+       if (ret) {
+               printk(KERN_WARNING"%s Erase block error %d at : %llx\n",
+                       map->name, ret, adr);
+               goto out;
+       }
+ out:  put_chip(map, chip);
+       spin_unlock(chip->mutex);
+       return ret;
+}
+
+static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len,
+                       size_t *retlen, u_char *buf)
+{
+       struct map_info *map = mtd->priv;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int chipnum = adr >> lpddr->chipshift;
+       struct flchip *chip = &lpddr->chips[chipnum];
+       int ret = 0;
+
+       spin_lock(chip->mutex);
+       ret = get_chip(map, chip, FL_READY);
+       if (ret) {
+               spin_unlock(chip->mutex);
+               return ret;
+       }
+
+       map_copy_from(map, buf, adr, len);
+       *retlen = len;
+
+       put_chip(map, chip);
+       spin_unlock(chip->mutex);
+       return ret;
+}
+
+static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
+                       size_t *retlen, void **mtdbuf, resource_size_t *phys)
+{
+       struct map_info *map = mtd->priv;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int chipnum = adr >> lpddr->chipshift;
+       unsigned long ofs, last_end = 0;
+       struct flchip *chip = &lpddr->chips[chipnum];
+       int ret = 0;
+
+       if (!map->virt || (adr + len > mtd->size))
+               return -EINVAL;
+
+       /* ofs: offset within the first chip that the first read should start */
+       ofs = adr - (chipnum << lpddr->chipshift);
+
+       *mtdbuf = (void *)map->virt + chip->start + ofs;
+       *retlen = 0;
+
+       while (len) {
+               unsigned long thislen;
+
+               if (chipnum >= lpddr->numchips)
+                       break;
+
+               /* We cannot point across chips that are virtually disjoint */
+               if (!last_end)
+                       last_end = chip->start;
+               else if (chip->start != last_end)
+                       break;
+
+               if ((len + ofs - 1) >> lpddr->chipshift)
+                       thislen = (1<<lpddr->chipshift) - ofs;
+               else
+                       thislen = len;
+               /* get the chip */
+               spin_lock(chip->mutex);
+               ret = get_chip(map, chip, FL_POINT);
+               spin_unlock(chip->mutex);
+               if (ret)
+                       break;
+
+               chip->state = FL_POINT;
+               chip->ref_point_counter++;
+               *retlen += thislen;
+               len -= thislen;
+
+               ofs = 0;
+               last_end += 1 << lpddr->chipshift;
+               chipnum++;
+               chip = &lpddr->chips[chipnum];
+       }
+       return 0;
+}
+
+static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
+{
+       struct map_info *map = mtd->priv;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int chipnum = adr >> lpddr->chipshift;
+       unsigned long ofs;
+
+       /* ofs: offset within the first chip that the first read should start */
+       ofs = adr - (chipnum << lpddr->chipshift);
+
+       while (len) {
+               unsigned long thislen;
+               struct flchip *chip;
+
+               chip = &lpddr->chips[chipnum];
+               if (chipnum >= lpddr->numchips)
+                       break;
+
+               if ((len + ofs - 1) >> lpddr->chipshift)
+                       thislen = (1<<lpddr->chipshift) - ofs;
+               else
+                       thislen = len;
+
+               spin_lock(chip->mutex);
+               if (chip->state == FL_POINT) {
+                       chip->ref_point_counter--;
+                       if (chip->ref_point_counter == 0)
+                               chip->state = FL_READY;
+               } else
+                       printk(KERN_WARNING "%s: Warning: unpoint called on non"
+                                       "pointed region\n", map->name);
+
+               put_chip(map, chip);
+               spin_unlock(chip->mutex);
+
+               len -= thislen;
+               ofs = 0;
+               chipnum++;
+       }
+}
+
+static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
+                               size_t *retlen, const u_char *buf)
+{
+       struct kvec vec;
+
+       vec.iov_base = (void *) buf;
+       vec.iov_len = len;
+
+       return lpddr_writev(mtd, &vec, 1, to, retlen);
+}
+
+
+static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs,
+                               unsigned long count, loff_t to, size_t *retlen)
+{
+       struct map_info *map = mtd->priv;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int ret = 0;
+       int chipnum;
+       unsigned long ofs, vec_seek, i;
+       int wbufsize = 1 << lpddr->qinfo->BufSizeShift;
+
+       size_t len = 0;
+
+       for (i = 0; i < count; i++)
+               len += vecs[i].iov_len;
+
+       *retlen = 0;
+       if (!len)
+               return 0;
+
+       chipnum = to >> lpddr->chipshift;
+
+       ofs = to;
+       vec_seek = 0;
+
+       do {
+               /* We must not cross write block boundaries */
+               int size = wbufsize - (ofs & (wbufsize-1));
+
+               if (size > len)
+                       size = len;
+
+               ret = do_write_buffer(map, &lpddr->chips[chipnum],
+                                         ofs, &vecs, &vec_seek, size);
+               if (ret)
+                       return ret;
+
+               ofs += size;
+               (*retlen) += size;
+               len -= size;
+
+               /* Be nice and reschedule with the chip in a usable
+                * state for other processes */
+               cond_resched();
+
+       } while (len);
+
+       return 0;
+}
+
+static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       unsigned long ofs, len;
+       int ret;
+       struct map_info *map = mtd->priv;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int size = 1 << lpddr->qinfo->UniformBlockSizeShift;
+
+       ofs = instr->addr;
+       len = instr->len;
+
+       if (ofs > mtd->size || (len + ofs) > mtd->size)
+               return -EINVAL;
+
+       while (len > 0) {
+               ret = do_erase_oneblock(mtd, ofs);
+               if (ret)
+                       return ret;
+               ofs += size;
+               len -= size;
+       }
+       instr->state = MTD_ERASE_DONE;
+       mtd_erase_callback(instr);
+
+       return 0;
+}
+
+#define DO_XXLOCK_LOCK         1
+#define DO_XXLOCK_UNLOCK       2
+int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk)
+{
+       int ret = 0;
+       struct map_info *map = mtd->priv;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int chipnum = adr >> lpddr->chipshift;
+       struct flchip *chip = &lpddr->chips[chipnum];
+
+       spin_lock(chip->mutex);
+       ret = get_chip(map, chip, FL_LOCKING);
+       if (ret) {
+               spin_unlock(chip->mutex);
+               return ret;
+       }
+
+       if (thunk == DO_XXLOCK_LOCK) {
+               send_pfow_command(map, LPDDR_LOCK_BLOCK, adr, adr + len, NULL);
+               chip->state = FL_LOCKING;
+       } else if (thunk == DO_XXLOCK_UNLOCK) {
+               send_pfow_command(map, LPDDR_UNLOCK_BLOCK, adr, adr + len, NULL);
+               chip->state = FL_UNLOCKING;
+       } else
+               BUG();
+
+       ret = wait_for_ready(map, chip, 1);
+       if (ret)        {
+               printk(KERN_ERR "%s: block unlock error status %d \n",
+                               map->name, ret);
+               goto out;
+       }
+out:   put_chip(map, chip);
+       spin_unlock(chip->mutex);
+       return ret;
+}
+
+static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+       return do_xxlock(mtd, ofs, len, DO_XXLOCK_LOCK);
+}
+
+static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+       return do_xxlock(mtd, ofs, len, DO_XXLOCK_UNLOCK);
+}
+
+int word_program(struct map_info *map, loff_t adr, uint32_t curval)
+{
+    int ret;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       int chipnum = adr >> lpddr->chipshift;
+       struct flchip *chip = &lpddr->chips[chipnum];
+
+       spin_lock(chip->mutex);
+       ret = get_chip(map, chip, FL_WRITING);
+       if (ret) {
+               spin_unlock(chip->mutex);
+               return ret;
+       }
+
+       send_pfow_command(map, LPDDR_WORD_PROGRAM, adr, 0x00, (map_word *)&curval);
+
+       ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->SingleWordProgTime));
+       if (ret)        {
+               printk(KERN_WARNING"%s word_program error at: %llx; val: %x\n",
+                       map->name, adr, curval);
+               goto out;
+       }
+
+out:   put_chip(map, chip);
+       spin_unlock(chip->mutex);
+       return ret;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexey Korolev <akorolev@infradead.org>");
+MODULE_DESCRIPTION("MTD driver for LPDDR flash chips");
diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c
new file mode 100644 (file)
index 0000000..79bf40f
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Probing flash chips with QINFO records.
+ * (C) 2008 Korolev Alexey <akorolev@infradead.org>
+ * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/mtd/xip.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/pfow.h>
+#include <linux/mtd/qinfo.h>
+
+static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr);
+struct mtd_info *lpddr_probe(struct map_info *map);
+static struct lpddr_private *lpddr_probe_chip(struct map_info *map);
+static int lpddr_pfow_present(struct map_info *map,
+                       struct lpddr_private *lpddr);
+
+static struct qinfo_query_info qinfo_array[] = {
+       /* General device info */
+       {0, 0, "DevSizeShift", "Device size 2^n bytes"},
+       {0, 3, "BufSizeShift", "Program buffer size 2^n bytes"},
+       /* Erase block information */
+       {1, 1, "TotalBlocksNum", "Total number of blocks"},
+       {1, 2, "UniformBlockSizeShift", "Uniform block size 2^n bytes"},
+       /* Partition information */
+       {2, 1, "HWPartsNum", "Number of hardware partitions"},
+       /* Optional features */
+       {5, 1, "SuspEraseSupp", "Suspend erase supported"},
+       /* Operation typical time */
+       {10, 0, "SingleWordProgTime", "Single word program 2^n u-sec"},
+       {10, 1, "ProgBufferTime", "Program buffer write 2^n u-sec"},
+       {10, 2, "BlockEraseTime", "Block erase 2^n m-sec"},
+       {10, 3, "FullChipEraseTime", "Full chip erase 2^n m-sec"},
+};
+
+static long lpddr_get_qinforec_pos(struct map_info *map, char *id_str)
+{
+       int qinfo_lines = sizeof(qinfo_array)/sizeof(struct qinfo_query_info);
+       int i;
+       int bankwidth = map_bankwidth(map) * 8;
+       int major, minor;
+
+       for (i = 0; i < qinfo_lines; i++) {
+               if (strcmp(id_str, qinfo_array[i].id_str) == 0) {
+                       major = qinfo_array[i].major & ((1 << bankwidth) - 1);
+                       minor = qinfo_array[i].minor & ((1 << bankwidth) - 1);
+                       return minor | (major << bankwidth);
+               }
+       }
+       printk(KERN_ERR"%s qinfo id string is wrong! \n", map->name);
+       BUG();
+       return -1;
+}
+
+static uint16_t lpddr_info_query(struct map_info *map, char *id_str)
+{
+       unsigned int dsr, val;
+       int bits_per_chip = map_bankwidth(map) * 8;
+       unsigned long adr = lpddr_get_qinforec_pos(map, id_str);
+       int attempts = 20;
+
+       /* Write a request for the PFOW record */
+       map_write(map, CMD(LPDDR_INFO_QUERY),
+                       map->pfow_base + PFOW_COMMAND_CODE);
+       map_write(map, CMD(adr & ((1 << bits_per_chip) - 1)),
+                       map->pfow_base + PFOW_COMMAND_ADDRESS_L);
+       map_write(map, CMD(adr >> bits_per_chip),
+                       map->pfow_base + PFOW_COMMAND_ADDRESS_H);
+       map_write(map, CMD(LPDDR_START_EXECUTION),
+                       map->pfow_base + PFOW_COMMAND_EXECUTE);
+
+       while ((attempts--) > 0) {
+               dsr = CMDVAL(map_read(map, map->pfow_base + PFOW_DSR));
+               if (dsr & DSR_READY_STATUS)
+                       break;
+               udelay(10);
+       }
+
+       val = CMDVAL(map_read(map, map->pfow_base + PFOW_COMMAND_DATA));
+       return val;
+}
+
+static int lpddr_pfow_present(struct map_info *map, struct lpddr_private *lpddr)
+{
+       map_word pfow_val[4];
+
+       /* Check identification string */
+       pfow_val[0] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_P);
+       pfow_val[1] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_F);
+       pfow_val[2] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_O);
+       pfow_val[3] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_W);
+
+       if (!map_word_equal(map, CMD('P'), pfow_val[0]))
+               goto out;
+
+       if (!map_word_equal(map, CMD('F'), pfow_val[1]))
+               goto out;
+
+       if (!map_word_equal(map, CMD('O'), pfow_val[2]))
+               goto out;
+
+       if (!map_word_equal(map, CMD('W'), pfow_val[3]))
+               goto out;
+
+       return 1;       /* "PFOW" is found */
+out:
+       printk(KERN_WARNING"%s: PFOW string at 0x%lx is not found \n",
+                                       map->name, map->pfow_base);
+       return 0;
+}
+
+static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr)
+{
+
+       lpddr->qinfo = kmalloc(sizeof(struct qinfo_chip), GFP_KERNEL);
+       if (!lpddr->qinfo) {
+               printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n",
+                               map->name);
+               return 0;
+       }
+       memset(lpddr->qinfo, 0, sizeof(struct qinfo_chip));
+
+       /* Get the ManuID */
+       lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID));
+       /* Get the DeviceID */
+       lpddr->DevId = CMDVAL(map_read(map, map->pfow_base + PFOW_DEVICE_ID));
+       /* read parameters from chip qinfo table */
+       lpddr->qinfo->DevSizeShift = lpddr_info_query(map, "DevSizeShift");
+       lpddr->qinfo->TotalBlocksNum = lpddr_info_query(map, "TotalBlocksNum");
+       lpddr->qinfo->BufSizeShift = lpddr_info_query(map, "BufSizeShift");
+       lpddr->qinfo->HWPartsNum = lpddr_info_query(map, "HWPartsNum");
+       lpddr->qinfo->UniformBlockSizeShift =
+                               lpddr_info_query(map, "UniformBlockSizeShift");
+       lpddr->qinfo->SuspEraseSupp = lpddr_info_query(map, "SuspEraseSupp");
+       lpddr->qinfo->SingleWordProgTime =
+                               lpddr_info_query(map, "SingleWordProgTime");
+       lpddr->qinfo->ProgBufferTime = lpddr_info_query(map, "ProgBufferTime");
+       lpddr->qinfo->BlockEraseTime = lpddr_info_query(map, "BlockEraseTime");
+       return 1;
+}
+static struct lpddr_private *lpddr_probe_chip(struct map_info *map)
+{
+       struct lpddr_private lpddr;
+       struct lpddr_private *retlpddr;
+       int numvirtchips;
+
+
+       if ((map->pfow_base + 0x1000) >= map->size) {
+               printk(KERN_NOTICE"%s Probe at base (0x%08lx) past the end of"
+                               "the map(0x%08lx)\n", map->name,
+                               (unsigned long)map->pfow_base, map->size - 1);
+               return NULL;
+       }
+       memset(&lpddr, 0, sizeof(struct lpddr_private));
+       if (!lpddr_pfow_present(map, &lpddr))
+               return NULL;
+
+       if (!lpddr_chip_setup(map, &lpddr))
+               return NULL;
+
+       /* Ok so we found a chip */
+       lpddr.chipshift = lpddr.qinfo->DevSizeShift;
+       lpddr.numchips = 1;
+
+       numvirtchips = lpddr.numchips * lpddr.qinfo->HWPartsNum;
+       retlpddr = kmalloc(sizeof(struct lpddr_private) +
+                       numvirtchips * sizeof(struct flchip), GFP_KERNEL);
+       if (!retlpddr)
+               return NULL;
+
+       memset(retlpddr, 0, sizeof(struct lpddr_private) +
+                               numvirtchips * sizeof(struct flchip));
+       memcpy(retlpddr, &lpddr, sizeof(struct lpddr_private));
+
+       retlpddr->numchips = numvirtchips;
+       retlpddr->chipshift = retlpddr->qinfo->DevSizeShift -
+                               __ffs(retlpddr->qinfo->HWPartsNum);
+
+       return retlpddr;
+}
+
+struct mtd_info *lpddr_probe(struct map_info *map)
+{
+       struct mtd_info *mtd = NULL;
+       struct lpddr_private *lpddr;
+
+       /* First probe the map to see if we havecan open PFOW here */
+       lpddr = lpddr_probe_chip(map);
+       if (!lpddr)
+               return NULL;
+
+       map->fldrv_priv = lpddr;
+       mtd = lpddr_cmdset(map);
+       if (mtd) {
+               if (mtd->size > map->size) {
+                       printk(KERN_WARNING "Reducing visibility of %ldKiB chip"
+                               "to %ldKiB\n", (unsigned long)mtd->size >> 10,
+                               (unsigned long)map->size >> 10);
+                       mtd->size = map->size;
+               }
+               return mtd;
+       }
+
+       kfree(lpddr->qinfo);
+       kfree(lpddr);
+       map->fldrv_priv = NULL;
+       return NULL;
+}
+
+static struct mtd_chip_driver lpddr_chipdrv = {
+       .probe          = lpddr_probe,
+       .name           = "qinfo_probe",
+       .module         = THIS_MODULE
+};
+
+static int __init lpddr_probe_init(void)
+{
+       register_mtd_chip_driver(&lpddr_chipdrv);
+       return 0;
+}
+
+static void __exit lpddr_probe_exit(void)
+{
+       unregister_mtd_chip_driver(&lpddr_chipdrv);
+}
+
+module_init(lpddr_probe_init);
+module_exit(lpddr_probe_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vasiliy Leonenko <vasiliy.leonenko@gmail.com>");
+MODULE_DESCRIPTION("Driver to probe qinfo flash chips");
+
index 5ea1693621642b84fbcbe482202395d493b0a91b..0225cbbf22de3ceb21b14a6bb8cf05cb3987817a 100644 (file)
@@ -10,8 +10,8 @@ config MTD_COMPLEX_MAPPINGS
          paged mappings of flash chips.
 
 config MTD_PHYSMAP
-       tristate "CFI Flash device in physical memory map"
-       depends on MTD_CFI || MTD_JEDECPROBE || MTD_ROM
+       tristate "Flash device in physical memory map"
+       depends on MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_LPDDR
        help
          This provides a 'mapping' driver which allows the NOR Flash and
          ROM driver code to communicate with chips which are mapped
@@ -23,9 +23,20 @@ config MTD_PHYSMAP
          To compile this driver as a module, choose M here: the
          module will be called physmap.
 
+config MTD_PHYSMAP_COMPAT
+       bool "Physmap compat support"
+       depends on MTD_PHYSMAP
+       default n
+       help
+         Setup a simple mapping via the Kconfig options.  Normally the
+         physmap configuration options are done via your board's
+         resource file.
+
+         If unsure, say N here.
+
 config MTD_PHYSMAP_START
        hex "Physical start address of flash mapping"
-       depends on MTD_PHYSMAP
+       depends on MTD_PHYSMAP_COMPAT
        default "0x8000000"
        help
          This is the physical memory location at which the flash chips
@@ -37,7 +48,7 @@ config MTD_PHYSMAP_START
 
 config MTD_PHYSMAP_LEN
        hex "Physical length of flash mapping"
-       depends on MTD_PHYSMAP
+       depends on MTD_PHYSMAP_COMPAT
        default "0"
        help
          This is the total length of the mapping of the flash chips on
@@ -51,7 +62,7 @@ config MTD_PHYSMAP_LEN
 
 config MTD_PHYSMAP_BANKWIDTH
        int "Bank width in octets"
-       depends on MTD_PHYSMAP
+       depends on MTD_PHYSMAP_COMPAT
        default "2"
        help
          This is the total width of the data bus of the flash devices
index 82811bcb04369840e083d81e15f60dfcd7ec9fbb..845ad4f2a5428dda6de39286921607cd4b0f7b53 100644 (file)
@@ -111,7 +111,7 @@ static struct mtd_partition alchemy_partitions[] = {
 
 static struct mtd_info *mymtd;
 
-int __init alchemy_mtd_init(void)
+static int __init alchemy_mtd_init(void)
 {
        struct mtd_partition *parts;
        int nb_parts = 0;
index d1eec7d3243f39ea0bb7e2e0b9c0f299e11ee1ce..237733d094c41feb17170d97ff1a72ce7c657350 100644 (file)
@@ -232,8 +232,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
                /* Trim the size if we are larger than the map */
                if (map->mtd->size > map->map.size) {
                        printk(KERN_WARNING MOD_NAME
-                               " rom(%u) larger than window(%lu). fixing...\n",
-                               map->mtd->size, map->map.size);
+                               " rom(%llu) larger than window(%lu). fixing...\n",
+                               (unsigned long long)map->mtd->size, map->map.size);
                        map->mtd->size = map->map.size;
                }
                if (window->rsrc.parent) {
index 0ecc3f6d735bfb78a2b8ad467dc2d69cb16b50ac..b4ed8161191846e95a65fd973ff20f4c3b5b4aa6 100644 (file)
@@ -88,7 +88,7 @@ struct mtd_partition flagadm_parts[] = {
 
 static struct mtd_info *mymtd;
 
-int __init init_flagadm(void)
+static int __init init_flagadm(void)
 {
        printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n",
                        FLASH_SIZE, FLASH_PHYS_ADDR);
index 1a6feb4474ded02f2fb4381c088efe3dd310a024..5f7a245ed13294b5038aaed76e4a1383debc49a6 100644 (file)
@@ -263,8 +263,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev,
                /* Trim the size if we are larger than the map */
                if (map->mtd->size > map->map.size) {
                        printk(KERN_WARNING MOD_NAME
-                               " rom(%u) larger than window(%lu). fixing...\n",
-                               map->mtd->size, map->map.size);
+                               " rom(%llu) larger than window(%lu). fixing...\n",
+                               (unsigned long long)map->mtd->size, map->map.size);
                        map->mtd->size = map->map.size;
                }
                if (window->rsrc.parent) {
index e115667bf1d04083b50d5ea18df34d55f34af7a4..cfacfa6f45ddd8708801765b5a35aff10dabec45 100644 (file)
@@ -69,7 +69,7 @@ struct map_info dbox2_flash_map = {
        .phys           = WINDOW_ADDR,
 };
 
-int __init init_dbox2_flash(void)
+static int __init init_dbox2_flash(void)
 {
                printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR);
        dbox2_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
index 9433738c1664178071690e484c4f346886ee4f0e..be9e90b44587a45c7fce5be7659de52f7613dca9 100644 (file)
@@ -71,7 +71,7 @@ static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
 static int                   mtd_parts_nb = 0;
 static struct mtd_partition *mtd_parts    = 0;
 
-int __init init_edb7312nor(void)
+static int __init init_edb7312nor(void)
 {
        static const char *rom_probe_types[] = PROBETYPES;
        const char **type;
index bbbcdd4c8d13d7f757c435538bc2f1ccc63953db..11a2f57df9cf5d8c87f0a5f907a239d409f7e544 100644 (file)
@@ -324,8 +324,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev,
                /* Trim the size if we are larger than the map */
                if (map->mtd->size > map->map.size) {
                        printk(KERN_WARNING MOD_NAME
-                               " rom(%u) larger than window(%lu). fixing...\n",
-                               map->mtd->size, map->map.size);
+                               " rom(%llu) larger than window(%lu). fixing...\n",
+                               (unsigned long long)map->mtd->size, map->map.size);
                        map->mtd->size = map->map.size;
                }
                if (window->rsrc.parent) {
index a8e3fde4cbd557f9feb18f61d8b114b5a267fb56..1e43124d498bc1cd2a685ad099db3713f136394f 100644 (file)
@@ -181,7 +181,7 @@ __setup("MTD_Partition=", MTD_New_Partition);
 /* Backwards-spelling-compatibility */
 __setup("MTD_Partion=", MTD_New_Partition);
 
-int __init init_fortunet(void)
+static int __init init_fortunet(void)
 {
        int     ix,iy;
        for(iy=ix=0;ix<MAX_NUM_REGIONS;ix++)
index 3b959fad1c4ec082bfd7310f789e673de214ac47..72c724fa8c276a3c6e6698e7d31eb4f60f9cc3bb 100644 (file)
@@ -65,7 +65,7 @@ static const char *probes[] = { "cmdlinepart", NULL };
 /*
  * Initialize FLASH support
  */
-int __init h720x_mtd_init(void)
+static int __init h720x_mtd_init(void)
 {
 
        char    *part_type = NULL;
index aeb6c916e23f8f6925dda4152055c8417241b78e..c32bc28920b35fe3927f960a528a52fd2f6fa5c6 100644 (file)
@@ -258,8 +258,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
                /* Trim the size if we are larger than the map */
                if (map->mtd->size > map->map.size) {
                        printk(KERN_WARNING MOD_NAME
-                               " rom(%u) larger than window(%lu). fixing...\n",
-                               map->mtd->size, map->map.size);
+                               " rom(%llu) larger than window(%lu). fixing...\n",
+                               (unsigned long long)map->mtd->size, map->map.size);
                        map->mtd->size = map->map.size;
                }
                if (window->rsrc.parent) {
index 2682ab51a36786732fdd08b83b8cde83f331ad62..998a27da97f315bf77f91e670fef6fd5905f68d3 100644 (file)
@@ -70,7 +70,7 @@ static struct mtd_partition *mtd_parts[NUM_FLASHBANKS];
 
 static const char *probes[] = { "cmdlinepart", NULL };
 
-int __init init_impa7(void)
+static int __init init_impa7(void)
 {
        static const char *rom_probe_types[] = PROBETYPES;
        const char **type;
index ed58f6a77bd92771d118223f6a31825a831bb7b3..748c85f635f1b340523b9cab6511455369c770e0 100644 (file)
@@ -202,7 +202,7 @@ static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
 
 static int __init h1900_special_case(void);
 
-int __init ipaq_mtd_init(void)
+static int __init ipaq_mtd_init(void)
 {
        struct mtd_partition *parts = NULL;
        int nb_parts = 0;
index 706f67394b073cb97f8e5894bd88129de4f858cd..0eb5a7c853806147092f990162faa2926a60344f 100644 (file)
@@ -55,7 +55,7 @@ struct map_info mbx_map = {
        .bankwidth = 4,
 };
 
-int __init init_mbx(void)
+static int __init init_mbx(void)
 {
        printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR);
        mbx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
index 965e6c6d6ab022237d523c224eb47b13830d67c3..a97133eb9d70ec8356e991d3a63fb888000d4c31 100644 (file)
@@ -226,7 +226,7 @@ static int __init nettel_init(void)
 
        if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) {
                printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n",
-                       amd_mtd->size>>10);
+                       (int)(amd_mtd->size>>10));
 
                amd_mtd->owner = THIS_MODULE;
 
@@ -357,13 +357,12 @@ static int __init nettel_init(void)
                *intel1par = 0;
        }
 
-       printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n",
-               (intel_mtd->size >> 10));
+       printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %lldKiB\n",
+              (unsigned long long)(intel_mtd->size >> 10));
 
        intel_mtd->owner = THIS_MODULE;
 
-       num_intel_partitions = sizeof(nettel_intel_partitions) /
-               sizeof(nettel_intel_partitions[0]);
+       num_intel_partitions = ARRAY_SIZE(nettel_intel_partitions);
 
        if (intelboot) {
                /*
index 43e04c1d22a9dd74140f51e3f42092b7b950d0d4..2b2e45093218fca47a36a80fba14f6505421e67c 100644 (file)
@@ -184,7 +184,7 @@ void cleanup_oct5066(void)
        release_region(PAGE_IO, 1);
 }
 
-int __init init_oct5066(void)
+static int __init init_oct5066(void)
 {
        int i;
        int ret = 0;
index 1db16e549e38422ddb9cc6dee3707a1f00b5195a..87743661d48ebfc4e43f317f5ceddee6e9397fe8 100644 (file)
@@ -29,7 +29,6 @@ struct physmap_flash_info {
        struct map_info         map[MAX_RESOURCES];
 #ifdef CONFIG_MTD_PARTITIONS
        int                     nr_parts;
-       struct mtd_partition    *parts;
 #endif
 };
 
@@ -56,14 +55,10 @@ static int physmap_flash_remove(struct platform_device *dev)
        for (i = 0; i < MAX_RESOURCES; i++) {
                if (info->mtd[i] != NULL) {
 #ifdef CONFIG_MTD_PARTITIONS
-                       if (info->nr_parts) {
+                       if (info->nr_parts || physmap_data->nr_parts)
                                del_mtd_partitions(info->mtd[i]);
-                               kfree(info->parts);
-                       } else if (physmap_data->nr_parts) {
-                               del_mtd_partitions(info->mtd[i]);
-                       } else {
+                       else
                                del_mtd_device(info->mtd[i]);
-                       }
 #else
                        del_mtd_device(info->mtd[i]);
 #endif
@@ -73,7 +68,12 @@ static int physmap_flash_remove(struct platform_device *dev)
        return 0;
 }
 
-static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
+static const char *rom_probe_types[] = {
+                                       "cfi_probe",
+                                       "jedec_probe",
+                                       "qinfo_probe",
+                                       "map_rom",
+                                       NULL };
 #ifdef CONFIG_MTD_PARTITIONS
 static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
 #endif
@@ -86,6 +86,9 @@ static int physmap_flash_probe(struct platform_device *dev)
        int err = 0;
        int i;
        int devices_found = 0;
+#ifdef CONFIG_MTD_PARTITIONS
+       struct mtd_partition *parts;
+#endif
 
        physmap_data = dev->dev.platform_data;
        if (physmap_data == NULL)
@@ -119,6 +122,7 @@ static int physmap_flash_probe(struct platform_device *dev)
                info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1;
                info->map[i].bankwidth = physmap_data->width;
                info->map[i].set_vpp = physmap_data->set_vpp;
+               info->map[i].pfow_base = physmap_data->pfow_base;
 
                info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys,
                                                 info->map[i].size);
@@ -163,9 +167,10 @@ static int physmap_flash_probe(struct platform_device *dev)
                goto err_out;
 
 #ifdef CONFIG_MTD_PARTITIONS
-       err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0);
+       err = parse_mtd_partitions(info->cmtd, part_probe_types, &parts, 0);
        if (err > 0) {
-               add_mtd_partitions(info->cmtd, info->parts, err);
+               add_mtd_partitions(info->cmtd, parts, err);
+               kfree(parts);
                return 0;
        }
 
@@ -251,14 +256,7 @@ static struct platform_driver physmap_flash_driver = {
 };
 
 
-#ifdef CONFIG_MTD_PHYSMAP_LEN
-#if CONFIG_MTD_PHYSMAP_LEN != 0
-#warning using PHYSMAP compat code
-#define PHYSMAP_COMPAT
-#endif
-#endif
-
-#ifdef PHYSMAP_COMPAT
+#ifdef CONFIG_MTD_PHYSMAP_COMPAT
 static struct physmap_flash_data physmap_flash_data = {
        .width          = CONFIG_MTD_PHYSMAP_BANKWIDTH,
 };
@@ -302,7 +300,7 @@ static int __init physmap_init(void)
        int err;
 
        err = platform_driver_register(&physmap_flash_driver);
-#ifdef PHYSMAP_COMPAT
+#ifdef CONFIG_MTD_PHYSMAP_COMPAT
        if (err == 0)
                platform_device_register(&physmap_flash);
 #endif
@@ -312,7 +310,7 @@ static int __init physmap_init(void)
 
 static void __exit physmap_exit(void)
 {
-#ifdef PHYSMAP_COMPAT
+#ifdef CONFIG_MTD_PHYSMAP_COMPAT
        platform_device_unregister(&physmap_flash);
 #endif
        platform_driver_unregister(&physmap_flash_driver);
@@ -326,8 +324,7 @@ MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 MODULE_DESCRIPTION("Generic configurable MTD map driver");
 
 /* legacy platform drivers can't hotplug or coldplg */
-#ifndef PHYSMAP_COMPAT
+#ifndef CONFIG_MTD_PHYSMAP_COMPAT
 /* work with hotplug and coldplug */
 MODULE_ALIAS("platform:physmap-flash");
 #endif
-
index f43ba2815cbbda28a27676d054058dc56e2b0369..4768bd5459d631e4b8a6ee76eb30c03843274b50 100644 (file)
@@ -48,7 +48,7 @@ static int fcnt;
 
 #define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n", __func__, __LINE__)
 
-int __init init_msp_flash(void)
+static int __init init_msp_flash(void)
 {
        int i, j;
        int offset, coff;
index de002eb1a7fe3a943753b2622a2dd64f7cea909a..933c0b63b0167d78eea816471f67e9fe9c12b9ab 100644 (file)
@@ -122,7 +122,7 @@ struct map_info redwood_flash_map = {
 
 static struct mtd_info *redwood_mtd;
 
-int __init init_redwood_flash(void)
+static int __init init_redwood_flash(void)
 {
        int err;
 
index 14d90edb443076d3759e98f66464b70ff22ad172..3e3ef53d4fd4cba637d9d378daca278c021809f4 100644 (file)
@@ -23,7 +23,7 @@ static struct map_info rpxlite_map = {
        .phys = WINDOW_ADDR,
 };
 
-int __init init_rpxlite(void)
+static int __init init_rpxlite(void)
 {
        printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
        rpxlite_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
index 6e1e99cd2b59c443715450c03b7f446261ac1776..d5374cdcb163bc82b3bbaa94a87062431be9cf8e 100644 (file)
@@ -136,7 +136,7 @@ static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS];
 #endif /* CONFIG_MTD_PARTITIONS */
 
 
-int __init init_sbc8240_mtd (void)
+static int __init init_sbc8240_mtd (void)
 {
        static struct _cjs {
                u_long addr;
index 21169e6d646c0df26318fdd22176915e515fa244..7e329f09a548f06bb83c36c1281d65db2278ef7c 100644 (file)
@@ -118,7 +118,8 @@ scb2_fixup_mtd(struct mtd_info *mtd)
                struct mtd_erase_region_info *region = &mtd->eraseregions[i];
 
                if (region->numblocks * region->erasesize > mtd->size) {
-                       region->numblocks = (mtd->size / region->erasesize);
+                       region->numblocks = ((unsigned long)mtd->size /
+                                               region->erasesize);
                        done = 1;
                } else {
                        region->numblocks = 0;
@@ -187,8 +188,9 @@ scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent)
                return -ENODEV;
        }
 
-       printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n",
-              scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size);
+       printk(KERN_NOTICE MODNAME ": chip size 0x%llx at offset 0x%llx\n",
+              (unsigned long long)scb2_mtd->size,
+              (unsigned long long)(SCB2_WINDOW - scb2_mtd->size));
 
        add_mtd_device(scb2_mtd);
 
index 026eab028189a170ebab7c7d013a6da86b4981d3..b392f096c7061921005c2d62acadbde184bdd4b9 100644 (file)
@@ -47,7 +47,7 @@ static struct mtd_partition sharpsl_partitions[1] = {
        }
 };
 
-int __init init_sharpsl(void)
+static int __init init_sharpsl(void)
 {
        struct mtd_partition *parts;
        int nb_parts = 0;
index a5d3d8531faa1aa22c1fbf8a4d442c4ef7402862..60146984f4be1b87f510095f19834f138a02294a 100644 (file)
@@ -109,7 +109,7 @@ static struct mtd_partition tqm8xxl_fs_partitions[] = {
 };
 #endif
 
-int __init init_tqm_mtd(void)
+static int __init init_tqm_mtd(void)
 {
        int idx = 0, ret = 0;
        unsigned long flash_addr, flash_size, mtd_size = 0;
index 0dc645f8152f21c90031d86443eb604f1af97957..81756e397711f4a53972000472d0ef284f9903e0 100644 (file)
@@ -51,7 +51,7 @@ int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
 
 /****************************************************************************/
 
-int __init uclinux_mtd_init(void)
+static int __init uclinux_mtd_init(void)
 {
        struct mtd_info *mtd;
        struct map_info *mapp;
@@ -94,7 +94,7 @@ int __init uclinux_mtd_init(void)
 
 /****************************************************************************/
 
-void __exit uclinux_mtd_cleanup(void)
+static void __exit uclinux_mtd_cleanup(void)
 {
        if (uclinux_ram_mtdinfo) {
                del_mtd_partitions(uclinux_ram_mtdinfo);
index 5a0c9a353b0fa63021834321afa2eab691c6864f..6d452dcdfe34756656e2014739ccdabeb9d29064 100644 (file)
@@ -146,7 +146,7 @@ static void __exit cleanup_vmax301(void)
        iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
 }
 
-int __init init_vmax301(void)
+static int __init init_vmax301(void)
 {
        int i;
        unsigned long iomapadr;
index 413b0cf9bbd2f6e3e234f661cd1f16c80e6f9a7e..933a2b6598b46fe44f1935afe3f55ed49f35f521 100644 (file)
@@ -74,7 +74,7 @@ do {                                                          \
        }                                                       \
 } while (0);
 
-int __init init_sbc82xx_flash(void)
+static int __init init_sbc82xx_flash(void)
 {
        volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl;
        int bigflash;
index bcffeda2df3d680babc66906151dd3641ec57208..e9ec59e9a566ab2a7560e9a166cfaa506077703f 100644 (file)
@@ -450,16 +450,20 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                if (!erase)
                        ret = -ENOMEM;
                else {
+                       struct erase_info_user einfo;
+
                        wait_queue_head_t waitq;
                        DECLARE_WAITQUEUE(wait, current);
 
                        init_waitqueue_head(&waitq);
 
-                       if (copy_from_user(&erase->addr, argp,
+                       if (copy_from_user(&einfo, argp,
                                    sizeof(struct erase_info_user))) {
                                kfree(erase);
                                return -EFAULT;
                        }
+                       erase->addr = einfo.start;
+                       erase->len = einfo.length;
                        erase->mtd = mtd;
                        erase->callback = mtdchar_erase_callback;
                        erase->priv = (unsigned long)&waitq;
index 1a05cf37851e137177708a1874ae1053cb963bc1..3dbb1b38db666cf4294047ec8cac5f8610c19525 100644 (file)
@@ -197,7 +197,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
                        continue;
                }
 
-               size = min(total_len, (size_t)(subdev->size - to));
+               size = min_t(uint64_t, total_len, subdev->size - to);
                wsize = size; /* store for future use */
 
                entry_high = entry_low;
@@ -385,7 +385,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
        struct mtd_concat *concat = CONCAT(mtd);
        struct mtd_info *subdev;
        int i, err;
-       u_int32_t length, offset = 0;
+       uint64_t length, offset = 0;
        struct erase_info *erase;
 
        if (!(mtd->flags & MTD_WRITEABLE))
@@ -518,7 +518,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
        return 0;
 }
 
-static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_concat *concat = CONCAT(mtd);
        int i, err = -EINVAL;
@@ -528,7 +528,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
 
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
-               size_t size;
+               uint64_t size;
 
                if (ofs >= subdev->size) {
                        size = 0;
@@ -556,7 +556,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
        return err;
 }
 
-static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_concat *concat = CONCAT(mtd);
        int i, err = 0;
@@ -566,7 +566,7 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
-               size_t size;
+               uint64_t size;
 
                if (ofs >= subdev->size) {
                        size = 0;
@@ -696,7 +696,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
        int i;
        size_t size;
        struct mtd_concat *concat;
-       u_int32_t max_erasesize, curr_erasesize;
+       uint32_t max_erasesize, curr_erasesize;
        int num_erase_region;
 
        printk(KERN_NOTICE "Concatenating MTD devices:\n");
@@ -842,12 +842,14 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],     /* subdevices to c
                concat->mtd.erasesize = curr_erasesize;
                concat->mtd.numeraseregions = 0;
        } else {
+               uint64_t tmp64;
+
                /*
                 * erase block size varies across the subdevices: allocate
                 * space to store the data describing the variable erase regions
                 */
                struct mtd_erase_region_info *erase_region_p;
-               u_int32_t begin, position;
+               uint64_t begin, position;
 
                concat->mtd.erasesize = max_erasesize;
                concat->mtd.numeraseregions = num_erase_region;
@@ -879,8 +881,9 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
                                        erase_region_p->offset = begin;
                                        erase_region_p->erasesize =
                                            curr_erasesize;
-                                       erase_region_p->numblocks =
-                                           (position - begin) / curr_erasesize;
+                                       tmp64 = position - begin;
+                                       do_div(tmp64, curr_erasesize);
+                                       erase_region_p->numblocks = tmp64;
                                        begin = position;
 
                                        curr_erasesize = subdev[i]->erasesize;
@@ -897,9 +900,9 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
                                                erase_region_p->offset = begin;
                                                erase_region_p->erasesize =
                                                    curr_erasesize;
-                                               erase_region_p->numblocks =
-                                                   (position -
-                                                    begin) / curr_erasesize;
+                                               tmp64 = position - begin;
+                                               do_div(tmp64, curr_erasesize);
+                                               erase_region_p->numblocks = tmp64;
                                                begin = position;
 
                                                curr_erasesize =
@@ -909,14 +912,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],     /* subdevices to c
                                        }
                                        position +=
                                            subdev[i]->eraseregions[j].
-                                           numblocks * curr_erasesize;
+                                           numblocks * (uint64_t)curr_erasesize;
                                }
                        }
                }
                /* Now write the final entry */
                erase_region_p->offset = begin;
                erase_region_p->erasesize = curr_erasesize;
-               erase_region_p->numblocks = (position - begin) / curr_erasesize;
+               tmp64 = position - begin;
+               do_div(tmp64, curr_erasesize);
+               erase_region_p->numblocks = tmp64;
        }
 
        return &concat->mtd;
index a9d246949820a9b0b61aef86061ddf341be21f92..76fe0a1e7a5e26534749a49436665b1d7ebcd086 100644 (file)
@@ -57,6 +57,19 @@ int add_mtd_device(struct mtd_info *mtd)
                        mtd->index = i;
                        mtd->usecount = 0;
 
+                       if (is_power_of_2(mtd->erasesize))
+                               mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
+                       else
+                               mtd->erasesize_shift = 0;
+
+                       if (is_power_of_2(mtd->writesize))
+                               mtd->writesize_shift = ffs(mtd->writesize) - 1;
+                       else
+                               mtd->writesize_shift = 0;
+
+                       mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
+                       mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
+
                        /* Some chips always power up locked. Unlock them now */
                        if ((mtd->flags & MTD_WRITEABLE)
                            && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {
@@ -344,7 +357,8 @@ static inline int mtd_proc_info (char *buf, int i)
        if (!this)
                return 0;
 
-       return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size,
+       return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", i,
+                      (unsigned long long)this->size,
                       this->erasesize, this->name);
 }
 
index aebb3b27edbd7554135b7e4899850eb19f929b28..1a6b3beabe8d04f70762f9aa3e470160a8e7c653 100644 (file)
@@ -80,9 +80,9 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
        if (ret) {
                set_current_state(TASK_RUNNING);
                remove_wait_queue(&wait_q, &wait);
-               printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] "
+               printk (KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] "
                                     "on \"%s\" failed\n",
-                       erase.addr, erase.len, mtd->name);
+                       (unsigned long long)erase.addr, (unsigned long long)erase.len, mtd->name);
                return ret;
        }
 
@@ -289,7 +289,10 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
        }
 
        cxt->mtd = mtd;
-       cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE;
+       if (mtd->size > INT_MAX)
+               cxt->oops_pages = INT_MAX / OOPS_PAGE_SIZE;
+       else
+               cxt->oops_pages = (int)mtd->size / OOPS_PAGE_SIZE;
 
        find_next_position(cxt);
 
index 3728913fa5fa553ebc358f0bd6792f8c74ec0699..144e6b613a77cebdaf848707d8ad5c7abf4add89 100644 (file)
@@ -26,7 +26,7 @@ static LIST_HEAD(mtd_partitions);
 struct mtd_part {
        struct mtd_info mtd;
        struct mtd_info *master;
-       u_int32_t offset;
+       uint64_t offset;
        int index;
        struct list_head list;
        int registered;
@@ -235,7 +235,7 @@ void mtd_erase_callback(struct erase_info *instr)
 }
 EXPORT_SYMBOL_GPL(mtd_erase_callback);
 
-static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_part *part = PART(mtd);
        if ((len + ofs) > mtd->size)
@@ -243,7 +243,7 @@ static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
        return part->master->lock(part->master, ofs + part->offset, len);
 }
 
-static int part_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_part *part = PART(mtd);
        if ((len + ofs) > mtd->size)
@@ -317,7 +317,7 @@ EXPORT_SYMBOL(del_mtd_partitions);
 
 static struct mtd_part *add_one_partition(struct mtd_info *master,
                const struct mtd_partition *part, int partno,
-               u_int32_t cur_offset)
+               uint64_t cur_offset)
 {
        struct mtd_part *slave;
 
@@ -395,19 +395,19 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
                slave->offset = cur_offset;
        if (slave->offset == MTDPART_OFS_NXTBLK) {
                slave->offset = cur_offset;
-               if ((cur_offset % master->erasesize) != 0) {
+               if (mtd_mod_by_eb(cur_offset, master) != 0) {
                        /* Round up to next erasesize */
-                       slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize;
+                       slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
                        printk(KERN_NOTICE "Moving partition %d: "
-                              "0x%08x -> 0x%08x\n", partno,
-                              cur_offset, slave->offset);
+                              "0x%012llx -> 0x%012llx\n", partno,
+                              (unsigned long long)cur_offset, (unsigned long long)slave->offset);
                }
        }
        if (slave->mtd.size == MTDPART_SIZ_FULL)
                slave->mtd.size = master->size - slave->offset;
 
-       printk(KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,
-               slave->offset + slave->mtd.size, slave->mtd.name);
+       printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
+               (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
 
        /* let's do some sanity checks */
        if (slave->offset >= master->size) {
@@ -420,13 +420,13 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
        }
        if (slave->offset + slave->mtd.size > master->size) {
                slave->mtd.size = master->size - slave->offset;
-               printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",
-                       part->name, master->name, slave->mtd.size);
+               printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
+                       part->name, master->name, (unsigned long long)slave->mtd.size);
        }
        if (master->numeraseregions > 1) {
                /* Deal with variable erase size stuff */
                int i, max = master->numeraseregions;
-               u32 end = slave->offset + slave->mtd.size;
+               u64 end = slave->offset + slave->mtd.size;
                struct mtd_erase_region_info *regions = master->eraseregions;
 
                /* Find the first erase regions which is part of this
@@ -449,7 +449,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
        }
 
        if ((slave->mtd.flags & MTD_WRITEABLE) &&
-           (slave->offset % slave->mtd.erasesize)) {
+           mtd_mod_by_eb(slave->offset, &slave->mtd)) {
                /* Doesn't start on a boundary of major erase size */
                /* FIXME: Let it be writable if it is on a boundary of
                 * _minor_ erase size though */
@@ -458,7 +458,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
                        part->name);
        }
        if ((slave->mtd.flags & MTD_WRITEABLE) &&
-           (slave->mtd.size % slave->mtd.erasesize)) {
+           mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
                slave->mtd.flags &= ~MTD_WRITEABLE;
                printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
                        part->name);
@@ -466,7 +466,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
 
        slave->mtd.ecclayout = master->ecclayout;
        if (master->block_isbad) {
-               uint32_t offs = 0;
+               uint64_t offs = 0;
 
                while (offs < slave->mtd.size) {
                        if (master->block_isbad(master,
@@ -501,7 +501,7 @@ int add_mtd_partitions(struct mtd_info *master,
                       int nbparts)
 {
        struct mtd_part *slave;
-       u_int32_t cur_offset = 0;
+       uint64_t cur_offset = 0;
        int i;
 
        printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
index f8ae0400c49c0bf16d312ded2d0958e5d1b26501..8b12e6e109d3721d12d8aff35d8a225ddd6873be 100644 (file)
@@ -163,6 +163,13 @@ config MTD_NAND_S3C2410_HWECC
          incorrect ECC generation, and if using these, the default of
          software ECC is preferable.
 
+config MTD_NAND_NDFC
+       tristate "NDFC NanD Flash Controller"
+       depends on 4xx
+       select MTD_NAND_ECC_SMC
+       help
+        NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
+
 config MTD_NAND_S3C2410_CLKSTOP
        bool "S3C2410 NAND IDLE clock stop"
        depends on MTD_NAND_S3C2410
index 9623803948550a066ea336049c844db8c5f1e7f8..6d9649159a18ad49f88c3e89e88df39ce3906864 100644 (file)
@@ -676,11 +676,11 @@ static int alauda_probe(struct usb_interface *interface,
                goto error;
 
        al->write_out = usb_sndbulkpipe(al->dev,
-                       ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+                       usb_endpoint_num(ep_wr));
        al->bulk_in = usb_rcvbulkpipe(al->dev,
-                       ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+                       usb_endpoint_num(ep_in));
        al->bulk_out = usb_sndbulkpipe(al->dev,
-                       ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+                       usb_endpoint_num(ep_out));
 
        /* second device is identical up to now */
        memcpy(al+1, al, sizeof(*al));
index b8064bf3aee43d45f086ddbf3675fae71a696c25..22a6b2e50e917467dbacb08f45775934d5a51832 100644 (file)
@@ -90,7 +90,7 @@ static int timing[3];
 module_param_array(timing, int, &numtimings, 0644);
 
 #ifdef CONFIG_MTD_PARTITIONS
-static const char *part_probes[] = { "RedBoot", NULL };
+static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
 #endif
 
 /* Hrm. Why isn't this already conditional on something in the struct device? */
@@ -805,10 +805,13 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
        add_mtd_device(mtd);
 
 #ifdef CONFIG_MTD_PARTITIONS
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+       mtd->name = "cafe_nand";
+#endif
        nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0);
        if (nr_parts > 0) {
                cafe->parts = parts;
-               dev_info(&cafe->pdev->dev, "%d RedBoot partitions found\n", nr_parts);
+               dev_info(&cafe->pdev->dev, "%d partitions found\n", nr_parts);
                add_mtd_partitions(mtd, parts, nr_parts);
        }
 #endif
index 4aa5bd6158daf8060c8dcb7f7e06dc9286c64203..65929db29446094807455e079c608ae4d2ec416c 100644 (file)
@@ -777,7 +777,9 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
        /* Fill in fsl_elbc_mtd structure */
        priv->mtd.priv = chip;
        priv->mtd.owner = THIS_MODULE;
-       priv->fmr = 0; /* rest filled in later */
+
+       /* Set the ECCM according to the settings in bootloader.*/
+       priv->fmr = in_be32(&lbc->fmr) & FMR_ECCM;
 
        /* fill in nand_chip structure */
        /* set up function call table */
index 0a9c9cd33f96b0f15f7f1d6f87260baaf9fb05df..0c3afccde8a238167d1466b7bd13f317c9b496c1 100644 (file)
@@ -2014,13 +2014,14 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
 int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                    int allowbbt)
 {
-       int page, len, status, pages_per_block, ret, chipnr;
+       int page, status, pages_per_block, ret, chipnr;
        struct nand_chip *chip = mtd->priv;
-       int rewrite_bbt[NAND_MAX_CHIPS]={0};
+       loff_t rewrite_bbt[NAND_MAX_CHIPS]={0};
        unsigned int bbt_masked_page = 0xffffffff;
+       loff_t len;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n",
-             (unsigned int)instr->addr, (unsigned int)instr->len);
+       DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n",
+             (unsigned long long)instr->addr, (unsigned long long)instr->len);
 
        /* Start address must align on block boundary */
        if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
@@ -2116,7 +2117,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                        DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
                              "Failed erase, page 0x%08x\n", page);
                        instr->state = MTD_ERASE_FAILED;
-                       instr->fail_addr = (page << chip->page_shift);
+                       instr->fail_addr =
+                               ((loff_t)page << chip->page_shift);
                        goto erase_exit;
                }
 
@@ -2126,7 +2128,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                 */
                if (bbt_masked_page != 0xffffffff &&
                    (page & BBT_PAGE_MASK) == bbt_masked_page)
-                           rewrite_bbt[chipnr] = (page << chip->page_shift);
+                           rewrite_bbt[chipnr] =
+                                       ((loff_t)page << chip->page_shift);
 
                /* Increment page address and decrement length */
                len -= (1 << chip->phys_erase_shift);
@@ -2173,7 +2176,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                        continue;
                /* update the BBT for chip */
                DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
-                     "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
+                     "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
                      chip->bbt_td->pages[chipnr]);
                nand_update_bbt(mtd, rewrite_bbt[chipnr]);
        }
@@ -2365,7 +2368,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        if (!mtd->name)
                mtd->name = type->name;
 
-       chip->chipsize = type->chipsize << 20;
+       chip->chipsize = (uint64_t)type->chipsize << 20;
 
        /* Newer devices have all the information in additional id bytes */
        if (!type->pagesize) {
@@ -2423,7 +2426,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 
        chip->bbt_erase_shift = chip->phys_erase_shift =
                ffs(mtd->erasesize) - 1;
-       chip->chip_shift = ffs(chip->chipsize) - 1;
+       if (chip->chipsize & 0xffffffff)
+               chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
+       else
+               chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1;
 
        /* Set the bad block position */
        chip->badblockpos = mtd->writesize > 512 ?
@@ -2517,7 +2523,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 /**
  * nand_scan_tail - [NAND Interface] Scan for the NAND device
  * @mtd:           MTD device structure
- * @maxchips:      Number of chips to scan for
  *
  * This is the second phase of the normal nand_scan() function. It
  * fills out all the uninitialized function pointers with the defaults
index 0b1c48595f1255547aea9eae6e41ba03e47dc05f..55c23e5cd21072b97a8401370f5d5f12a9e4394e 100644 (file)
@@ -171,16 +171,16 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
                                if (tmp == msk)
                                        continue;
                                if (reserved_block_code && (tmp == reserved_block_code)) {
-                                       printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
-                                              ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+                                       printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n",
+                                              (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
                                        this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
                                        mtd->ecc_stats.bbtblocks++;
                                        continue;
                                }
                                /* Leave it for now, if its matured we can move this
                                 * message to MTD_DEBUG_LEVEL0 */
-                               printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
-                                      ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+                               printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n",
+                                      (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
                                /* Factory marked bad or worn out ? */
                                if (tmp == 0)
                                        this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
@@ -284,7 +284,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
 
        /* Read the primary version, if available */
        if (td->options & NAND_BBT_VERSION) {
-               scan_read_raw(mtd, buf, td->pages[0] << this->page_shift,
+               scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
                              mtd->writesize);
                td->version[0] = buf[mtd->writesize + td->veroffs];
                printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
@@ -293,7 +293,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
 
        /* Read the mirror version, if available */
        if (md && (md->options & NAND_BBT_VERSION)) {
-               scan_read_raw(mtd, buf, md->pages[0] << this->page_shift,
+               scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
                              mtd->writesize);
                md->version[0] = buf[mtd->writesize + md->veroffs];
                printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
@@ -411,7 +411,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
                numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
                startblock = chip * numblocks;
                numblocks += startblock;
-               from = startblock << (this->bbt_erase_shift - 1);
+               from = (loff_t)startblock << (this->bbt_erase_shift - 1);
        }
 
        for (i = startblock; i < numblocks;) {
@@ -428,8 +428,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
 
                if (ret) {
                        this->bbt[i >> 3] |= 0x03 << (i & 0x6);
-                       printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
-                              i >> 1, (unsigned int)from);
+                       printk(KERN_WARNING "Bad eraseblock %d at 0x%012llx\n",
+                              i >> 1, (unsigned long long)from);
                        mtd->ecc_stats.badblocks++;
                }
 
@@ -495,7 +495,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
                for (block = 0; block < td->maxblocks; block++) {
 
                        int actblock = startblock + dir * block;
-                       loff_t offs = actblock << this->bbt_erase_shift;
+                       loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
 
                        /* Read first page */
                        scan_read_raw(mtd, buf, offs, mtd->writesize);
@@ -719,7 +719,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
 
                memset(&einfo, 0, sizeof(einfo));
                einfo.mtd = mtd;
-               einfo.addr = (unsigned long)to;
+               einfo.addr = to;
                einfo.len = 1 << this->bbt_erase_shift;
                res = nand_erase_nand(mtd, &einfo, 1);
                if (res < 0)
@@ -729,8 +729,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                if (res < 0)
                        goto outerr;
 
-               printk(KERN_DEBUG "Bad block table written to 0x%08x, version "
-                      "0x%02X\n", (unsigned int)to, td->version[chip]);
+               printk(KERN_DEBUG "Bad block table written to 0x%012llx, version "
+                      "0x%02X\n", (unsigned long long)to, td->version[chip]);
 
                /* Mark it as used */
                td->pages[chip] = page;
@@ -910,7 +910,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
                        newval = oldval | (0x2 << (block & 0x06));
                        this->bbt[(block >> 3)] = newval;
                        if ((oldval != newval) && td->reserved_block_code)
-                               nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1));
+                               nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1));
                        continue;
                }
                update = 0;
@@ -931,7 +931,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
                   new ones have been marked, then we need to update the stored
                   bbts.  This should only happen once. */
                if (update && td->reserved_block_code)
-                       nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1));
+                       nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1));
        }
 }
 
@@ -1027,7 +1027,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
        if (!this->bbt || !td)
                return -EINVAL;
 
-       len = mtd->size >> (this->bbt_erase_shift + 2);
        /* Allocate a temporary buffer for one eraseblock incl. oob */
        len = (1 << this->bbt_erase_shift);
        len += (len >> this->page_shift) * mtd->oobsize;
index ae7c57781a68e76885ca286b9a68a16d218358be..cd0711b83ac4bca101e34bcba578af5c76fac0cf 100644 (file)
@@ -38,6 +38,9 @@
 #include <linux/delay.h>
 #include <linux/list.h>
 #include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
 
 /* Default simulator parameters values */
 #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE)  || \
@@ -100,6 +103,7 @@ static unsigned int bitflips = 0;
 static char *gravepages = NULL;
 static unsigned int rptwear = 0;
 static unsigned int overridesize = 0;
+static char *cache_file = NULL;
 
 module_param(first_id_byte,  uint, 0400);
 module_param(second_id_byte, uint, 0400);
@@ -122,12 +126,13 @@ module_param(bitflips,       uint, 0400);
 module_param(gravepages,     charp, 0400);
 module_param(rptwear,        uint, 0400);
 module_param(overridesize,   uint, 0400);
+module_param(cache_file,     charp, 0400);
 
 MODULE_PARM_DESC(first_id_byte,  "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
 MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
 MODULE_PARM_DESC(third_id_byte,  "The third byte returned by NAND Flash 'read ID' command");
 MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
-MODULE_PARM_DESC(access_delay,   "Initial page access delay (microiseconds)");
+MODULE_PARM_DESC(access_delay,   "Initial page access delay (microseconds)");
 MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
 MODULE_PARM_DESC(erase_delay,    "Sector erase delay (milliseconds)");
 MODULE_PARM_DESC(output_cycle,   "Word output (from flash) time (nanodeconds)");
@@ -153,6 +158,7 @@ MODULE_PARM_DESC(rptwear,        "Number of erases inbetween reporting wear, if
 MODULE_PARM_DESC(overridesize,   "Specifies the NAND Flash size overriding the ID bytes. "
                                 "The size is specified in erase blocks and as the exponent of a power of two"
                                 " e.g. 5 means a size of 32 erase blocks");
+MODULE_PARM_DESC(cache_file,     "File to use to cache nand pages instead of memory");
 
 /* The largest possible page size */
 #define NS_LARGEST_PAGE_SIZE   2048
@@ -266,6 +272,9 @@ MODULE_PARM_DESC(overridesize,   "Specifies the NAND Flash size overriding the I
  */
 #define NS_MAX_PREVSTATES 1
 
+/* Maximum page cache pages needed to read or write a NAND page to the cache_file */
+#define NS_MAX_HELD_PAGES 16
+
 /*
  * A union to represent flash memory contents and flash buffer.
  */
@@ -295,6 +304,9 @@ struct nandsim {
        /* The simulated NAND flash pages array */
        union ns_mem *pages;
 
+       /* Slab allocator for nand pages */
+       struct kmem_cache *nand_pages_slab;
+
        /* Internal buffer of page + OOB size bytes */
        union ns_mem buf;
 
@@ -335,6 +347,13 @@ struct nandsim {
                 int ale; /* address Latch Enable */
                 int wp;  /* write Protect */
         } lines;
+
+       /* Fields needed when using a cache file */
+       struct file *cfile; /* Open file */
+       unsigned char *pages_written; /* Which pages have been written */
+       void *file_buf;
+       struct page *held_pages[NS_MAX_HELD_PAGES];
+       int held_cnt;
 };
 
 /*
@@ -420,25 +439,69 @@ static struct mtd_info *nsmtd;
 static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
 
 /*
- * Allocate array of page pointers and initialize the array to NULL
- * pointers.
+ * Allocate array of page pointers, create slab allocation for an array
+ * and initialize the array by NULL pointers.
  *
  * RETURNS: 0 if success, -ENOMEM if memory alloc fails.
  */
 static int alloc_device(struct nandsim *ns)
 {
-       int i;
+       struct file *cfile;
+       int i, err;
+
+       if (cache_file) {
+               cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
+               if (IS_ERR(cfile))
+                       return PTR_ERR(cfile);
+               if (!cfile->f_op || (!cfile->f_op->read && !cfile->f_op->aio_read)) {
+                       NS_ERR("alloc_device: cache file not readable\n");
+                       err = -EINVAL;
+                       goto err_close;
+               }
+               if (!cfile->f_op->write && !cfile->f_op->aio_write) {
+                       NS_ERR("alloc_device: cache file not writeable\n");
+                       err = -EINVAL;
+                       goto err_close;
+               }
+               ns->pages_written = vmalloc(ns->geom.pgnum);
+               if (!ns->pages_written) {
+                       NS_ERR("alloc_device: unable to allocate pages written array\n");
+                       err = -ENOMEM;
+                       goto err_close;
+               }
+               ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+               if (!ns->file_buf) {
+                       NS_ERR("alloc_device: unable to allocate file buf\n");
+                       err = -ENOMEM;
+                       goto err_free;
+               }
+               ns->cfile = cfile;
+               memset(ns->pages_written, 0, ns->geom.pgnum);
+               return 0;
+       }
 
        ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem));
        if (!ns->pages) {
-               NS_ERR("alloc_map: unable to allocate page array\n");
+               NS_ERR("alloc_device: unable to allocate page array\n");
                return -ENOMEM;
        }
        for (i = 0; i < ns->geom.pgnum; i++) {
                ns->pages[i].byte = NULL;
        }
+       ns->nand_pages_slab = kmem_cache_create("nandsim",
+                                               ns->geom.pgszoob, 0, 0, NULL);
+       if (!ns->nand_pages_slab) {
+               NS_ERR("cache_create: unable to create kmem_cache\n");
+               return -ENOMEM;
+       }
 
        return 0;
+
+err_free:
+       vfree(ns->pages_written);
+err_close:
+       filp_close(cfile, NULL);
+       return err;
 }
 
 /*
@@ -448,11 +511,20 @@ static void free_device(struct nandsim *ns)
 {
        int i;
 
+       if (ns->cfile) {
+               kfree(ns->file_buf);
+               vfree(ns->pages_written);
+               filp_close(ns->cfile, NULL);
+               return;
+       }
+
        if (ns->pages) {
                for (i = 0; i < ns->geom.pgnum; i++) {
                        if (ns->pages[i].byte)
-                               kfree(ns->pages[i].byte);
+                               kmem_cache_free(ns->nand_pages_slab,
+                                               ns->pages[i].byte);
                }
+               kmem_cache_destroy(ns->nand_pages_slab);
                vfree(ns->pages);
        }
 }
@@ -464,7 +536,7 @@ static char *get_partition_name(int i)
        return kstrdup(buf, GFP_KERNEL);
 }
 
-static u_int64_t divide(u_int64_t n, u_int32_t d)
+static uint64_t divide(uint64_t n, uint32_t d)
 {
        do_div(n, d);
        return n;
@@ -480,8 +552,8 @@ static int init_nandsim(struct mtd_info *mtd)
        struct nand_chip *chip = (struct nand_chip *)mtd->priv;
        struct nandsim   *ns   = (struct nandsim *)(chip->priv);
        int i, ret = 0;
-       u_int64_t remains;
-       u_int64_t next_offset;
+       uint64_t remains;
+       uint64_t next_offset;
 
        if (NS_IS_INITIALIZED(ns)) {
                NS_ERR("init_nandsim: nandsim is already initialized\n");
@@ -548,7 +620,7 @@ static int init_nandsim(struct mtd_info *mtd)
        remains = ns->geom.totsz;
        next_offset = 0;
        for (i = 0; i < parts_num; ++i) {
-               u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz;
+               uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz;
 
                if (!part_sz || part_sz > remains) {
                        NS_ERR("bad partition size.\n");
@@ -1211,6 +1283,97 @@ static int find_operation(struct nandsim *ns, uint32_t flag)
        return -1;
 }
 
+static void put_pages(struct nandsim *ns)
+{
+       int i;
+
+       for (i = 0; i < ns->held_cnt; i++)
+               page_cache_release(ns->held_pages[i]);
+}
+
+/* Get page cache pages in advance to provide NOFS memory allocation */
+static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos)
+{
+       pgoff_t index, start_index, end_index;
+       struct page *page;
+       struct address_space *mapping = file->f_mapping;
+
+       start_index = pos >> PAGE_CACHE_SHIFT;
+       end_index = (pos + count - 1) >> PAGE_CACHE_SHIFT;
+       if (end_index - start_index + 1 > NS_MAX_HELD_PAGES)
+               return -EINVAL;
+       ns->held_cnt = 0;
+       for (index = start_index; index <= end_index; index++) {
+               page = find_get_page(mapping, index);
+               if (page == NULL) {
+                       page = find_or_create_page(mapping, index, GFP_NOFS);
+                       if (page == NULL) {
+                               write_inode_now(mapping->host, 1);
+                               page = find_or_create_page(mapping, index, GFP_NOFS);
+                       }
+                       if (page == NULL) {
+                               put_pages(ns);
+                               return -ENOMEM;
+                       }
+                       unlock_page(page);
+               }
+               ns->held_pages[ns->held_cnt++] = page;
+       }
+       return 0;
+}
+
+static int set_memalloc(void)
+{
+       if (current->flags & PF_MEMALLOC)
+               return 0;
+       current->flags |= PF_MEMALLOC;
+       return 1;
+}
+
+static void clear_memalloc(int memalloc)
+{
+       if (memalloc)
+               current->flags &= ~PF_MEMALLOC;
+}
+
+static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos)
+{
+       mm_segment_t old_fs;
+       ssize_t tx;
+       int err, memalloc;
+
+       err = get_pages(ns, file, count, *pos);
+       if (err)
+               return err;
+       old_fs = get_fs();
+       set_fs(get_ds());
+       memalloc = set_memalloc();
+       tx = vfs_read(file, (char __user *)buf, count, pos);
+       clear_memalloc(memalloc);
+       set_fs(old_fs);
+       put_pages(ns);
+       return tx;
+}
+
+static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos)
+{
+       mm_segment_t old_fs;
+       ssize_t tx;
+       int err, memalloc;
+
+       err = get_pages(ns, file, count, *pos);
+       if (err)
+               return err;
+       old_fs = get_fs();
+       set_fs(get_ds());
+       memalloc = set_memalloc();
+       tx = vfs_write(file, (char __user *)buf, count, pos);
+       clear_memalloc(memalloc);
+       set_fs(old_fs);
+       put_pages(ns);
+       return tx;
+}
+
 /*
  * Returns a pointer to the current page.
  */
@@ -1227,6 +1390,38 @@ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
        return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
 }
 
+int do_read_error(struct nandsim *ns, int num)
+{
+       unsigned int page_no = ns->regs.row;
+
+       if (read_error(page_no)) {
+               int i;
+               memset(ns->buf.byte, 0xFF, num);
+               for (i = 0; i < num; ++i)
+                       ns->buf.byte[i] = random32();
+               NS_WARN("simulating read error in page %u\n", page_no);
+               return 1;
+       }
+       return 0;
+}
+
+void do_bit_flips(struct nandsim *ns, int num)
+{
+       if (bitflips && random32() < (1 << 22)) {
+               int flips = 1;
+               if (bitflips > 1)
+                       flips = (random32() % (int) bitflips) + 1;
+               while (flips--) {
+                       int pos = random32() % (num * 8);
+                       ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
+                       NS_WARN("read_page: flipping bit %d in page %d "
+                               "reading from %d ecc: corrected=%u failed=%u\n",
+                               pos, ns->regs.row, ns->regs.column + ns->regs.off,
+                               nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
+               }
+       }
+}
+
 /*
  * Fill the NAND buffer with data read from the specified page.
  */
@@ -1234,36 +1429,40 @@ static void read_page(struct nandsim *ns, int num)
 {
        union ns_mem *mypage;
 
+       if (ns->cfile) {
+               if (!ns->pages_written[ns->regs.row]) {
+                       NS_DBG("read_page: page %d not written\n", ns->regs.row);
+                       memset(ns->buf.byte, 0xFF, num);
+               } else {
+                       loff_t pos;
+                       ssize_t tx;
+
+                       NS_DBG("read_page: page %d written, reading from %d\n",
+                               ns->regs.row, ns->regs.column + ns->regs.off);
+                       if (do_read_error(ns, num))
+                               return;
+                       pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off;
+                       tx = read_file(ns, ns->cfile, ns->buf.byte, num, &pos);
+                       if (tx != num) {
+                               NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
+                               return;
+                       }
+                       do_bit_flips(ns, num);
+               }
+               return;
+       }
+
        mypage = NS_GET_PAGE(ns);
        if (mypage->byte == NULL) {
                NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
                memset(ns->buf.byte, 0xFF, num);
        } else {
-               unsigned int page_no = ns->regs.row;
                NS_DBG("read_page: page %d allocated, reading from %d\n",
                        ns->regs.row, ns->regs.column + ns->regs.off);
-               if (read_error(page_no)) {
-                       int i;
-                       memset(ns->buf.byte, 0xFF, num);
-                       for (i = 0; i < num; ++i)
-                               ns->buf.byte[i] = random32();
-                       NS_WARN("simulating read error in page %u\n", page_no);
+               if (do_read_error(ns, num))
                        return;
-               }
                memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
-               if (bitflips && random32() < (1 << 22)) {
-                       int flips = 1;
-                       if (bitflips > 1)
-                               flips = (random32() % (int) bitflips) + 1;
-                       while (flips--) {
-                               int pos = random32() % (num * 8);
-                               ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
-                               NS_WARN("read_page: flipping bit %d in page %d "
-                                       "reading from %d ecc: corrected=%u failed=%u\n",
-                                       pos, ns->regs.row, ns->regs.column + ns->regs.off,
-                                       nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
-                       }
-               }
+               do_bit_flips(ns, num);
        }
 }
 
@@ -1275,11 +1474,20 @@ static void erase_sector(struct nandsim *ns)
        union ns_mem *mypage;
        int i;
 
+       if (ns->cfile) {
+               for (i = 0; i < ns->geom.pgsec; i++)
+                       if (ns->pages_written[ns->regs.row + i]) {
+                               NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i);
+                               ns->pages_written[ns->regs.row + i] = 0;
+                       }
+               return;
+       }
+
        mypage = NS_GET_PAGE(ns);
        for (i = 0; i < ns->geom.pgsec; i++) {
                if (mypage->byte != NULL) {
                        NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
-                       kfree(mypage->byte);
+                       kmem_cache_free(ns->nand_pages_slab, mypage->byte);
                        mypage->byte = NULL;
                }
                mypage++;
@@ -1295,16 +1503,57 @@ static int prog_page(struct nandsim *ns, int num)
        union ns_mem *mypage;
        u_char *pg_off;
 
+       if (ns->cfile) {
+               loff_t off, pos;
+               ssize_t tx;
+               int all;
+
+               NS_DBG("prog_page: writing page %d\n", ns->regs.row);
+               pg_off = ns->file_buf + ns->regs.column + ns->regs.off;
+               off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off;
+               if (!ns->pages_written[ns->regs.row]) {
+                       all = 1;
+                       memset(ns->file_buf, 0xff, ns->geom.pgszoob);
+               } else {
+                       all = 0;
+                       pos = off;
+                       tx = read_file(ns, ns->cfile, pg_off, num, &pos);
+                       if (tx != num) {
+                               NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
+                               return -1;
+                       }
+               }
+               for (i = 0; i < num; i++)
+                       pg_off[i] &= ns->buf.byte[i];
+               if (all) {
+                       pos = (loff_t)ns->regs.row * ns->geom.pgszoob;
+                       tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, &pos);
+                       if (tx != ns->geom.pgszoob) {
+                               NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
+                               return -1;
+                       }
+                       ns->pages_written[ns->regs.row] = 1;
+               } else {
+                       pos = off;
+                       tx = write_file(ns, ns->cfile, pg_off, num, &pos);
+                       if (tx != num) {
+                               NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
+                               return -1;
+                       }
+               }
+               return 0;
+       }
+
        mypage = NS_GET_PAGE(ns);
        if (mypage->byte == NULL) {
                NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
                /*
                 * 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.
+                * then kernel memory alloc runs writeback which goes to the FS
+                * again and deadlocks. This was seen in practice.
                 */
-               mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS);
+               mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS);
                if (mypage->byte == NULL) {
                        NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
                        return -1;
@@ -1736,13 +1985,17 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
 
                /* Check if chip is expecting command */
                if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) {
-                       /*
-                        * We are in situation when something else (not command)
-                        * was expected but command was input. In this case ignore
-                        * previous command(s)/state(s) and accept the last one.
-                        */
-                       NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, "
-                               "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
+                       /* Do not warn if only 2 id bytes are read */
+                       if (!(ns->regs.command == NAND_CMD_READID &&
+                           NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) {
+                               /*
+                                * We are in situation when something else (not command)
+                                * was expected but command was input. In this case ignore
+                                * previous command(s)/state(s) and accept the last one.
+                                */
+                               NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, "
+                                       "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
+                       }
                        switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
                }
 
@@ -2044,7 +2297,7 @@ static int __init ns_init_module(void)
        }
 
        if (overridesize) {
-               u_int64_t new_size = (u_int64_t)nsmtd->erasesize << overridesize;
+               uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize;
                if (new_size >> overridesize != nsmtd->erasesize) {
                        NS_ERR("overridesize is too big\n");
                        goto err_exit;
index 955959eb02d49f301463e8b7ec998431df3bcfe8..582cf80f555a5ba99ac1a79abf1952b65114966d 100644 (file)
@@ -2,12 +2,20 @@
  *  drivers/mtd/ndfc.c
  *
  *  Overview:
- *   Platform independend driver for NDFC (NanD Flash Controller)
+ *   Platform independent driver for NDFC (NanD Flash Controller)
  *   integrated into EP440 cores
  *
+ *   Ported to an OF platform driver by Sean MacLennan
+ *
+ *   The NDFC supports multiple chips, but this driver only supports a
+ *   single chip since I do not have access to any boards with
+ *   multiple chips.
+ *
  *  Author: Thomas Gleixner
  *
  *  Copyright 2006 IBM
+ *  Copyright 2008 PIKA Technologies
+ *    Sean MacLennan <smaclennan@pikatech.com>
  *
  *  This program is free software; you can redistribute         it and/or modify it
  *  under  the terms of         the GNU General  Public License as published by the
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/ndfc.h>
 #include <linux/mtd/mtd.h>
-#include <linux/platform_device.h>
-
+#include <linux/of_platform.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;
-       struct nand_chip                chip;
-       struct platform_nand_chip       *pl_chip;
-};
 
-static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS];
 
 struct ndfc_controller {
-       void __iomem            *ndfcbase;
-       struct nand_hw_control  ndfc_control;
-       atomic_t                childs_active;
+       struct of_device *ofdev;
+       void __iomem *ndfcbase;
+       struct mtd_info mtd;
+       struct nand_chip chip;
+       int chip_select;
+       struct nand_hw_control ndfc_control;
+#ifdef CONFIG_MTD_PARTITIONS
+       struct mtd_partition *parts;
+#endif
 };
 
 static struct ndfc_controller ndfc_ctrl;
@@ -50,17 +51,14 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip)
 {
        uint32_t ccr;
        struct ndfc_controller *ndfc = &ndfc_ctrl;
-       struct nand_chip *nandchip = mtd->priv;
-       struct ndfc_nand_mtd *nandmtd = nandchip->priv;
-       struct platform_nand_chip *pchip = nandmtd->pl_chip;
 
-       ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
+       ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
        if (chip >= 0) {
                ccr &= ~NDFC_CCR_BS_MASK;
-               ccr |= NDFC_CCR_BS(chip + pchip->chip_offset);
+               ccr |= NDFC_CCR_BS(chip + ndfc->chip_select);
        } else
                ccr |= NDFC_CCR_RESET_CE;
-       __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
+       out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
 }
 
 static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
@@ -80,7 +78,7 @@ static int ndfc_ready(struct mtd_info *mtd)
 {
        struct ndfc_controller *ndfc = &ndfc_ctrl;
 
-       return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
+       return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
 }
 
 static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
@@ -88,9 +86,9 @@ static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
        uint32_t ccr;
        struct ndfc_controller *ndfc = &ndfc_ctrl;
 
-       ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
+       ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
        ccr |= NDFC_CCR_RESET_ECC;
-       __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
+       out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
        wmb();
 }
 
@@ -102,9 +100,10 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd,
        uint8_t *p = (uint8_t *)&ecc;
 
        wmb();
-       ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC);
-       ecc_code[0] = p[1];
-       ecc_code[1] = p[2];
+       ecc = in_be32(ndfc->ndfcbase + NDFC_ECC);
+       /* The NDFC uses Smart Media (SMC) bytes order */
+       ecc_code[0] = p[2];
+       ecc_code[1] = p[1];
        ecc_code[2] = p[3];
 
        return 0;
@@ -123,7 +122,7 @@ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
        uint32_t *p = (uint32_t *) buf;
 
        for(;len > 0; len -= 4)
-               *p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA);
+               *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA);
 }
 
 static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
@@ -132,7 +131,7 @@ static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
        uint32_t *p = (uint32_t *) buf;
 
        for(;len > 0; len -= 4)
-               __raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA);
+               out_be32(ndfc->ndfcbase + NDFC_DATA, *p++);
 }
 
 static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
@@ -141,7 +140,7 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
        uint32_t *p = (uint32_t *) buf;
 
        for(;len > 0; len -= 4)
-               if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA))
+               if (*p++ != in_be32(ndfc->ndfcbase + NDFC_DATA))
                        return -EFAULT;
        return 0;
 }
@@ -149,10 +148,19 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 /*
  * Initialize chip structure
  */
-static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
+static int ndfc_chip_init(struct ndfc_controller *ndfc,
+                         struct device_node *node)
 {
-       struct ndfc_controller *ndfc = &ndfc_ctrl;
-       struct nand_chip *chip = &mtd->chip;
+#ifdef CONFIG_MTD_PARTITIONS
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+       static const char *part_types[] = { "cmdlinepart", NULL };
+#else
+       static const char *part_types[] = { NULL };
+#endif
+#endif
+       struct device_node *flash_np;
+       struct nand_chip *chip = &ndfc->chip;
+       int ret;
 
        chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
        chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
@@ -160,8 +168,6 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
        chip->dev_ready = ndfc_ready;
        chip->select_chip = ndfc_select_chip;
        chip->chip_delay = 50;
-       chip->priv = mtd;
-       chip->options = mtd->pl_chip->options;
        chip->controller = &ndfc->ndfc_control;
        chip->read_buf = ndfc_read_buf;
        chip->write_buf = ndfc_write_buf;
@@ -172,143 +178,136 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
        chip->ecc.mode = NAND_ECC_HW;
        chip->ecc.size = 256;
        chip->ecc.bytes = 3;
-       chip->ecclayout = chip->ecc.layout = mtd->pl_chip->ecclayout;
-       mtd->mtd.priv = chip;
-       mtd->mtd.owner = THIS_MODULE;
-}
-
-static int ndfc_chip_probe(struct platform_device *pdev)
-{
-       struct platform_nand_chip *nc = pdev->dev.platform_data;
-       struct ndfc_chip_settings *settings = nc->priv;
-       struct ndfc_controller *ndfc = &ndfc_ctrl;
-       struct ndfc_nand_mtd *nandmtd;
-
-       if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS)
-               return -EINVAL;
-
-       /* Set the bank settings */
-       __raw_writel(settings->bank_settings,
-                    ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2));
 
-       nandmtd = &ndfc_mtd[pdev->id];
-       if (nandmtd->pl_chip)
-               return -EBUSY;
+       ndfc->mtd.priv = chip;
+       ndfc->mtd.owner = THIS_MODULE;
 
-       nandmtd->pl_chip = nc;
-       ndfc_chip_init(nandmtd);
-
-       /* Scan for chips */
-       if (nand_scan(&nandmtd->mtd, nc->nr_chips)) {
-               nandmtd->pl_chip = NULL;
+       flash_np = of_get_next_child(node, NULL);
+       if (!flash_np)
                return -ENODEV;
+
+       ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s",
+                                  ndfc->ofdev->dev.bus_id, flash_np->name);
+       if (!ndfc->mtd.name) {
+               ret = -ENOMEM;
+               goto err;
        }
 
-#ifdef CONFIG_MTD_PARTITIONS
-       printk("Number of partitions %d\n", nc->nr_partitions);
-       if (nc->nr_partitions) {
-               /* Add the full device, so complete dumps can be made */
-               add_mtd_device(&nandmtd->mtd);
-               add_mtd_partitions(&nandmtd->mtd, nc->partitions,
-                                  nc->nr_partitions);
+       ret = nand_scan(&ndfc->mtd, 1);
+       if (ret)
+               goto err;
 
-       } else
-#else
-               add_mtd_device(&nandmtd->mtd);
+#ifdef CONFIG_MTD_PARTITIONS
+       ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0);
+       if (ret < 0)
+               goto err;
+
+#ifdef CONFIG_MTD_OF_PARTS
+       if (ret == 0) {
+               ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np,
+                                             &ndfc->parts);
+               if (ret < 0)
+                       goto err;
+       }
 #endif
 
-       atomic_inc(&ndfc->childs_active);
-       return 0;
-}
+       if (ret > 0)
+               ret = add_mtd_partitions(&ndfc->mtd, ndfc->parts, ret);
+       else
+#endif
+               ret = add_mtd_device(&ndfc->mtd);
 
-static int ndfc_chip_remove(struct platform_device *pdev)
-{
-       return 0;
+err:
+       of_node_put(flash_np);
+       if (ret)
+               kfree(ndfc->mtd.name);
+       return ret;
 }
 
-static int ndfc_nand_probe(struct platform_device *pdev)
+static int __devinit ndfc_probe(struct of_device *ofdev,
+                               const struct of_device_id *match)
 {
-       struct platform_nand_ctrl *nc = pdev->dev.platform_data;
-       struct ndfc_controller_settings *settings = nc->priv;
-       struct resource *res = pdev->resource;
        struct ndfc_controller *ndfc = &ndfc_ctrl;
-       unsigned long long phys = settings->ndfc_erpn | res->start;
+       const u32 *reg;
+       u32 ccr;
+       int err, len;
 
-#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
+       spin_lock_init(&ndfc->ndfc_control.lock);
+       init_waitqueue_head(&ndfc->ndfc_control.wq);
+       ndfc->ofdev = ofdev;
+       dev_set_drvdata(&ofdev->dev, ndfc);
+
+       /* Read the reg property to get the chip select */
+       reg = of_get_property(ofdev->node, "reg", &len);
+       if (reg == NULL || len != 12) {
+               dev_err(&ofdev->dev, "unable read reg property (%d)\n", len);
+               return -ENOENT;
+       }
+       ndfc->chip_select = reg[0];
+
+       ndfc->ndfcbase = of_iomap(ofdev->node, 0);
        if (!ndfc->ndfcbase) {
-               printk(KERN_ERR "NDFC: ioremap failed\n");
+               dev_err(&ofdev->dev, "failed to get memory\n");
                return -EIO;
        }
 
-       __raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR);
+       ccr = NDFC_CCR_BS(ndfc->chip_select);
 
-       spin_lock_init(&ndfc->ndfc_control.lock);
-       init_waitqueue_head(&ndfc->ndfc_control.wq);
+       /* It is ok if ccr does not exist - just default to 0 */
+       reg = of_get_property(ofdev->node, "ccr", NULL);
+       if (reg)
+               ccr |= *reg;
 
-       platform_set_drvdata(pdev, ndfc);
+       out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
 
-       printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n",
-              __raw_readl(ndfc->ndfcbase + NDFC_REVID));
+       /* Set the bank settings if given */
+       reg = of_get_property(ofdev->node, "bank-settings", NULL);
+       if (reg) {
+               int offset = NDFC_BCFG0 + (ndfc->chip_select << 2);
+               out_be32(ndfc->ndfcbase + offset, *reg);
+       }
+
+       err = ndfc_chip_init(ndfc, ofdev->node);
+       if (err) {
+               iounmap(ndfc->ndfcbase);
+               return err;
+       }
 
        return 0;
 }
 
-static int ndfc_nand_remove(struct platform_device *pdev)
+static int __devexit ndfc_remove(struct of_device *ofdev)
 {
-       struct ndfc_controller *ndfc = platform_get_drvdata(pdev);
+       struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev);
 
-       if (atomic_read(&ndfc->childs_active))
-               return -EBUSY;
+       nand_release(&ndfc->mtd);
 
-       if (ndfc) {
-               platform_set_drvdata(pdev, NULL);
-               iounmap(ndfc_ctrl.ndfcbase);
-               ndfc_ctrl.ndfcbase = NULL;
-       }
        return 0;
 }
 
-/* driver device registration */
-
-static struct platform_driver ndfc_chip_driver = {
-       .probe          = ndfc_chip_probe,
-       .remove         = ndfc_chip_remove,
-       .driver         = {
-               .name   = "ndfc-chip",
-               .owner  = THIS_MODULE,
-       },
+static const struct of_device_id ndfc_match[] = {
+       { .compatible = "ibm,ndfc", },
+       {}
 };
+MODULE_DEVICE_TABLE(of, ndfc_match);
 
-static struct platform_driver ndfc_nand_driver = {
-       .probe          = ndfc_nand_probe,
-       .remove         = ndfc_nand_remove,
-       .driver         = {
-               .name   = "ndfc-nand",
-               .owner  = THIS_MODULE,
+static struct of_platform_driver ndfc_driver = {
+       .driver = {
+               .name   = "ndfc",
        },
+       .match_table = ndfc_match,
+       .probe = ndfc_probe,
+       .remove = __devexit_p(ndfc_remove),
 };
 
 static int __init ndfc_nand_init(void)
 {
-       int ret;
-
-       spin_lock_init(&ndfc_ctrl.ndfc_control.lock);
-       init_waitqueue_head(&ndfc_ctrl.ndfc_control.wq);
-
-       ret = platform_driver_register(&ndfc_nand_driver);
-       if (!ret)
-               ret = platform_driver_register(&ndfc_chip_driver);
-       return ret;
+       return of_register_platform_driver(&ndfc_driver);
 }
 
 static void __exit ndfc_nand_exit(void)
 {
-       platform_driver_unregister(&ndfc_chip_driver);
-       platform_driver_unregister(&ndfc_nand_driver);
+       of_unregister_platform_driver(&ndfc_driver);
 }
 
 module_init(ndfc_nand_init);
@@ -316,6 +315,4 @@ module_exit(ndfc_nand_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
-MODULE_DESCRIPTION("Platform driver for NDFC");
-MODULE_ALIAS("platform:ndfc-chip");
-MODULE_ALIAS("platform:ndfc-nand");
+MODULE_DESCRIPTION("OF Platform driver for NDFC");
index fc41444956108fd007fa818b3f80e658a718d3a6..cc55cbc2b308a058ed098732972ac92346627e6c 100644 (file)
@@ -298,7 +298,7 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = {
 #define NDTR1_tAR(c)   (min((c), 15) << 0)
 
 /* convert nano-seconds to nand flash controller clock cycles */
-#define ns2cycle(ns, clk)      (int)(((ns) * (clk / 1000000) / 1000) + 1)
+#define ns2cycle(ns, clk)      (int)(((ns) * (clk / 1000000) / 1000) - 1)
 
 static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
                                   const struct pxa3xx_nand_timing *t)
@@ -368,14 +368,14 @@ static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
                /* large block, 2 cycles for column address
                 * row address starts from 3rd cycle
                 */
-               info->ndcb1 |= (page_addr << 16) | (column & 0xffff);
+               info->ndcb1 |= page_addr << 16;
                if (info->row_addr_cycles == 3)
                        info->ndcb2 = (page_addr >> 16) & 0xff;
        } else
                /* small block, 1 cycles for column address
                 * row address starts from 2nd cycle
                 */
-               info->ndcb1 = (page_addr << 8) | (column & 0xff);
+               info->ndcb1 = page_addr << 8;
 
        if (cmd == cmdset->program)
                info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
index 30a518e211bdf9a1e6016b32799974ab453e0fd3..54ec7542a7b77590b4a3b961fef310d6aed2a9b7 100644 (file)
@@ -2,6 +2,7 @@
  * drivers/mtd/nand/sharpsl.c
  *
  *  Copyright (C) 2004 Richard Purdie
+ *  Copyright (C) 2008 Dmitry Baryshkov
  *
  *  Based on Sharp's NAND driver sharp_sl.c
  *
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand_ecc.h>
 #include <linux/mtd/partitions.h>
+#include <linux/mtd/sharpsl.h>
 #include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 
-static void __iomem *sharpsl_io_base;
-static int sharpsl_phys_base = 0x0C000000;
+struct sharpsl_nand {
+       struct mtd_info         mtd;
+       struct nand_chip        chip;
+
+       void __iomem            *io;
+};
+
+#define mtd_to_sharpsl(_mtd)   container_of(_mtd, struct sharpsl_nand, mtd)
 
 /* register offset */
-#define ECCLPLB                sharpsl_io_base+0x00    /* line parity 7 - 0 bit */
-#define ECCLPUB                sharpsl_io_base+0x04    /* line parity 15 - 8 bit */
-#define ECCCP          sharpsl_io_base+0x08    /* column parity 5 - 0 bit */
-#define ECCCNTR                sharpsl_io_base+0x0C    /* ECC byte counter */
-#define ECCCLRR                sharpsl_io_base+0x10    /* cleare ECC */
-#define FLASHIO                sharpsl_io_base+0x14    /* Flash I/O */
-#define FLASHCTL       sharpsl_io_base+0x18    /* Flash Control */
+#define ECCLPLB                0x00    /* line parity 7 - 0 bit */
+#define ECCLPUB                0x04    /* line parity 15 - 8 bit */
+#define ECCCP          0x08    /* column parity 5 - 0 bit */
+#define ECCCNTR                0x0C    /* ECC byte counter */
+#define ECCCLRR                0x10    /* cleare ECC */
+#define FLASHIO                0x14    /* Flash I/O */
+#define FLASHCTL       0x18    /* Flash Control */
 
 /* Flash control bit */
 #define FLRYBY         (1 << 5)
@@ -44,35 +54,6 @@ static int sharpsl_phys_base = 0x0C000000;
 #define FLCLE          (1 << 1)
 #define FLCE0          (1 << 0)
 
-/*
- * MTD structure for SharpSL
- */
-static struct mtd_info *sharpsl_mtd = NULL;
-
-/*
- * Define partitions for flash device
- */
-#define DEFAULT_NUM_PARTITIONS 3
-
-static int nr_partitions;
-static struct mtd_partition sharpsl_nand_default_partition_info[] = {
-       {
-        .name = "System Area",
-        .offset = 0,
-        .size = 7 * 1024 * 1024,
-        },
-       {
-        .name = "Root Filesystem",
-        .offset = 7 * 1024 * 1024,
-        .size = 30 * 1024 * 1024,
-        },
-       {
-        .name = "Home Filesystem",
-        .offset = MTDPART_OFS_APPEND,
-        .size = MTDPART_SIZ_FULL,
-        },
-};
-
 /*
  *     hardware specific access to control-lines
  *     ctrl:
@@ -84,6 +65,7 @@ static struct mtd_partition sharpsl_nand_default_partition_info[] = {
 static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd,
                                   unsigned int ctrl)
 {
+       struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
        struct nand_chip *chip = mtd->priv;
 
        if (ctrl & NAND_CTRL_CHANGE) {
@@ -93,103 +75,97 @@ static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd,
 
                bits ^= 0x11;
 
-               writeb((readb(FLASHCTL) & ~0x17) | bits, FLASHCTL);
+               writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL);
        }
 
        if (cmd != NAND_CMD_NONE)
                writeb(cmd, chip->IO_ADDR_W);
 }
 
-static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
-
-static struct nand_bbt_descr sharpsl_bbt = {
-       .options = 0,
-       .offs = 4,
-       .len = 2,
-       .pattern = scan_ff_pattern
-};
-
-static struct nand_bbt_descr sharpsl_akita_bbt = {
-       .options = 0,
-       .offs = 4,
-       .len = 1,
-       .pattern = scan_ff_pattern
-};
-
-static struct nand_ecclayout akita_oobinfo = {
-       .eccbytes = 24,
-       .eccpos = {
-                  0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11,
-                  0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
-                  0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37},
-       .oobfree = {{0x08, 0x09}}
-};
-
 static int sharpsl_nand_dev_ready(struct mtd_info *mtd)
 {
-       return !((readb(FLASHCTL) & FLRYBY) == 0);
+       struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
+       return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0);
 }
 
 static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode)
 {
-       writeb(0, ECCCLRR);
+       struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
+       writeb(0, sharpsl->io + ECCCLRR);
 }
 
 static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code)
 {
-       ecc_code[0] = ~readb(ECCLPUB);
-       ecc_code[1] = ~readb(ECCLPLB);
-       ecc_code[2] = (~readb(ECCCP) << 2) | 0x03;
-       return readb(ECCCNTR) != 0;
+       struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
+       ecc_code[0] = ~readb(sharpsl->io + ECCLPUB);
+       ecc_code[1] = ~readb(sharpsl->io + ECCLPLB);
+       ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03;
+       return readb(sharpsl->io + ECCCNTR) != 0;
 }
 
 #ifdef CONFIG_MTD_PARTITIONS
-const char *part_probes[] = { "cmdlinepart", NULL };
+static const char *part_probes[] = { "cmdlinepart", NULL };
 #endif
 
 /*
  * Main initialization routine
  */
-static int __init sharpsl_nand_init(void)
+static int __devinit sharpsl_nand_probe(struct platform_device *pdev)
 {
        struct nand_chip *this;
+#ifdef CONFIG_MTD_PARTITIONS
        struct mtd_partition *sharpsl_partition_info;
+       int nr_partitions;
+#endif
+       struct resource *r;
        int err = 0;
+       struct sharpsl_nand *sharpsl;
+       struct sharpsl_nand_platform_data *data = pdev->dev.platform_data;
+
+       if (!data) {
+               dev_err(&pdev->dev, "no platform data!\n");
+               return -EINVAL;
+       }
 
        /* Allocate memory for MTD device structure and private data */
-       sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
-       if (!sharpsl_mtd) {
+       sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL);
+       if (!sharpsl) {
                printk("Unable to allocate SharpSL NAND MTD device structure.\n");
                return -ENOMEM;
        }
 
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               dev_err(&pdev->dev, "no io memory resource defined!\n");
+               err = -ENODEV;
+               goto err_get_res;
+       }
+
        /* map physical address */
-       sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000);
-       if (!sharpsl_io_base) {
+       sharpsl->io = ioremap(r->start, resource_size(r));
+       if (!sharpsl->io) {
                printk("ioremap to access Sharp SL NAND chip failed\n");
-               kfree(sharpsl_mtd);
-               return -EIO;
+               err = -EIO;
+               goto err_ioremap;
        }
 
        /* Get pointer to private data */
-       this = (struct nand_chip *)(&sharpsl_mtd[1]);
-
-       /* Initialize structures */
-       memset(sharpsl_mtd, 0, sizeof(struct mtd_info));
-       memset(this, 0, sizeof(struct nand_chip));
+       this = (struct nand_chip *)(&sharpsl->chip);
 
        /* Link the private data with the MTD structure */
-       sharpsl_mtd->priv = this;
-       sharpsl_mtd->owner = THIS_MODULE;
+       sharpsl->mtd.priv = this;
+       sharpsl->mtd.owner = THIS_MODULE;
+
+       platform_set_drvdata(pdev, sharpsl);
 
        /*
         * PXA initialize
         */
-       writeb(readb(FLASHCTL) | FLWP, FLASHCTL);
+       writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL);
 
        /* Set address of NAND IO lines */
-       this->IO_ADDR_R = FLASHIO;
-       this->IO_ADDR_W = FLASHIO;
+       this->IO_ADDR_R = sharpsl->io + FLASHIO;
+       this->IO_ADDR_W = sharpsl->io + FLASHIO;
        /* Set address of hardware control function */
        this->cmd_ctrl = sharpsl_nand_hwcontrol;
        this->dev_ready = sharpsl_nand_dev_ready;
@@ -199,68 +175,89 @@ static int __init sharpsl_nand_init(void)
        this->ecc.mode = NAND_ECC_HW;
        this->ecc.size = 256;
        this->ecc.bytes = 3;
-       this->badblock_pattern = &sharpsl_bbt;
-       if (machine_is_akita() || machine_is_borzoi()) {
-               this->badblock_pattern = &sharpsl_akita_bbt;
-               this->ecc.layout = &akita_oobinfo;
-       }
+       this->badblock_pattern = data->badblock_pattern;
+       this->ecc.layout = data->ecc_layout;
        this->ecc.hwctl = sharpsl_nand_enable_hwecc;
        this->ecc.calculate = sharpsl_nand_calculate_ecc;
        this->ecc.correct = nand_correct_data;
 
        /* Scan to find existence of the device */
-       err = nand_scan(sharpsl_mtd, 1);
-       if (err) {
-               iounmap(sharpsl_io_base);
-               kfree(sharpsl_mtd);
-               return err;
-       }
+       err = nand_scan(&sharpsl->mtd, 1);
+       if (err)
+               goto err_scan;
 
        /* Register the partitions */
-       sharpsl_mtd->name = "sharpsl-nand";
-       nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, &sharpsl_partition_info, 0);
-
+       sharpsl->mtd.name = "sharpsl-nand";
+#ifdef CONFIG_MTD_PARTITIONS
+       nr_partitions = parse_mtd_partitions(&sharpsl->mtd, part_probes, &sharpsl_partition_info, 0);
        if (nr_partitions <= 0) {
-               nr_partitions = DEFAULT_NUM_PARTITIONS;
-               sharpsl_partition_info = sharpsl_nand_default_partition_info;
-               if (machine_is_poodle()) {
-                       sharpsl_partition_info[1].size = 22 * 1024 * 1024;
-               } else if (machine_is_corgi() || machine_is_shepherd()) {
-                       sharpsl_partition_info[1].size = 25 * 1024 * 1024;
-               } else if (machine_is_husky()) {
-                       sharpsl_partition_info[1].size = 53 * 1024 * 1024;
-               } else if (machine_is_spitz()) {
-                       sharpsl_partition_info[1].size = 5 * 1024 * 1024;
-               } else if (machine_is_akita()) {
-                       sharpsl_partition_info[1].size = 58 * 1024 * 1024;
-               } else if (machine_is_borzoi()) {
-                       sharpsl_partition_info[1].size = 32 * 1024 * 1024;
-               }
+               nr_partitions = data->nr_partitions;
+               sharpsl_partition_info = data->partitions;
        }
 
-       add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions);
+       if (nr_partitions > 0)
+               err = add_mtd_partitions(&sharpsl->mtd, sharpsl_partition_info, nr_partitions);
+       else
+#endif
+       err = add_mtd_device(&sharpsl->mtd);
+       if (err)
+               goto err_add;
 
        /* Return happy */
        return 0;
-}
 
-module_init(sharpsl_nand_init);
+err_add:
+       nand_release(&sharpsl->mtd);
+
+err_scan:
+       platform_set_drvdata(pdev, NULL);
+       iounmap(sharpsl->io);
+err_ioremap:
+err_get_res:
+       kfree(sharpsl);
+       return err;
+}
 
 /*
  * Clean up routine
  */
-static void __exit sharpsl_nand_cleanup(void)
+static int __devexit sharpsl_nand_remove(struct platform_device *pdev)
 {
+       struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev);
+
        /* Release resources, unregister device */
-       nand_release(sharpsl_mtd);
+       nand_release(&sharpsl->mtd);
 
-       iounmap(sharpsl_io_base);
+       platform_set_drvdata(pdev, NULL);
+
+       iounmap(sharpsl->io);
 
        /* Free the MTD device structure */
-       kfree(sharpsl_mtd);
+       kfree(sharpsl);
+
+       return 0;
+}
+
+static struct platform_driver sharpsl_nand_driver = {
+       .driver = {
+               .name   = "sharpsl-nand",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = sharpsl_nand_probe,
+       .remove         = __devexit_p(sharpsl_nand_remove),
+};
+
+static int __init sharpsl_nand_init(void)
+{
+       return platform_driver_register(&sharpsl_nand_driver);
 }
+module_init(sharpsl_nand_init);
 
-module_exit(sharpsl_nand_cleanup);
+static void __exit sharpsl_nand_exit(void)
+{
+       platform_driver_unregister(&sharpsl_nand_driver);
+}
+module_exit(sharpsl_nand_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
index 320b929abe795ea00eb5bf4c130f6db966d32893..d1c4546513f7ac88706646eb8e04083ec4bc465a 100644 (file)
@@ -39,7 +39,7 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        struct NFTLrecord *nftl;
        unsigned long temp;
 
-       if (mtd->type != MTD_NANDFLASH)
+       if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX)
                return;
        /* OK, this is moderately ugly.  But probably safe.  Alternatives? */
        if (memcmp(mtd->name, "DiskOnChip", 10))
index ccc4f209fbb5c17985ba5e264b0a6ddde9824fa3..8b22b1836e9fee9281565e2e89294ec4d2b47ad5 100644 (file)
@@ -51,7 +51,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
           the mtd device accordingly.  We could even get rid of
           nftl->EraseSize if there were any point in doing so. */
        nftl->EraseSize = nftl->mbd.mtd->erasesize;
-        nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
+        nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
 
        nftl->MediaUnit = BLOCK_NIL;
        nftl->SpareMediaUnit = BLOCK_NIL;
@@ -168,7 +168,7 @@ device is already correct.
                        printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
                               mh->UnitSizeFactor);
                        nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
-                       nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
+                       nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
                }
 #endif
                nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
index 90ed319f26e6843c98d26dafba79762b2fd4abbc..529af271db17486074ef44a6610bd365ace65d28 100644 (file)
@@ -1772,7 +1772,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
        int len;
        int ret = 0;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len);
 
        block_size = (1 << this->erase_shift);
 
@@ -1810,7 +1810,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 
                /* Check if we have a bad block, we do not erase bad blocks */
                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);
+                       printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%012llx\n", (unsigned long long) addr);
                        instr->state = MTD_ERASE_FAILED;
                        goto erase_exit;
                }
@@ -2029,7 +2029,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
  *
  * Lock one or more blocks
  */
-static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        int ret;
 
@@ -2047,7 +2047,7 @@ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
  *
  * Unlock one or more blocks
  */
-static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        int ret;
 
index e538c0a72abb0d531ddd992a559671d7020a7f5f..d2aa9c46530ff506ebad9a411522c67e0d838f54 100644 (file)
@@ -21,8 +21,6 @@
 
 #include <asm/types.h>
 
-#define const_cpu_to_le16      __constant_cpu_to_le16
-
 static int block_size = 0;
 module_param(block_size, int, 0);
 MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");
@@ -156,7 +154,7 @@ static int scan_header(struct partition *part)
        size_t retlen;
 
        sectors_per_block = part->block_size / SECTOR_SIZE;
-       part->total_blocks = part->mbd.mtd->size / part->block_size;
+       part->total_blocks = (u32)part->mbd.mtd->size / part->block_size;
 
        if (part->total_blocks < 2)
                return -ENOENT;
@@ -276,16 +274,17 @@ static void erase_callback(struct erase_info *erase)
 
        part = (struct partition*)erase->priv;
 
-       i = erase->addr / part->block_size;
-       if (i >= part->total_blocks || part->blocks[i].offset != erase->addr) {
-               printk(KERN_ERR PREFIX "erase callback for unknown offset %x "
-                               "on '%s'\n", erase->addr, part->mbd.mtd->name);
+       i = (u32)erase->addr / part->block_size;
+       if (i >= part->total_blocks || part->blocks[i].offset != erase->addr ||
+           erase->addr > UINT_MAX) {
+               printk(KERN_ERR PREFIX "erase callback for unknown offset %llx "
+                               "on '%s'\n", (unsigned long long)erase->addr, part->mbd.mtd->name);
                return;
        }
 
        if (erase->state != MTD_ERASE_DONE) {
-               printk(KERN_WARNING PREFIX "erase failed at 0x%x on '%s', "
-                               "state %d\n", erase->addr,
+               printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', "
+                               "state %d\n", (unsigned long long)erase->addr,
                                part->mbd.mtd->name, erase->state);
 
                part->blocks[i].state = BLOCK_FAILED;
@@ -297,7 +296,7 @@ static void erase_callback(struct erase_info *erase)
                return;
        }
 
-       magic = const_cpu_to_le16(RFD_MAGIC);
+       magic = cpu_to_le16(RFD_MAGIC);
 
        part->blocks[i].state = BLOCK_ERASED;
        part->blocks[i].free_sectors = part->data_sectors_per_block;
@@ -345,9 +344,9 @@ static int erase_block(struct partition *part, int block)
        rc = part->mbd.mtd->erase(part->mbd.mtd, erase);
 
        if (rc) {
-               printk(KERN_ERR PREFIX "erase of region %x,%x on '%s' "
-                               "failed\n", erase->addr, erase->len,
-                               part->mbd.mtd->name);
+               printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
+                               "failed\n", (unsigned long long)erase->addr,
+                               (unsigned long long)erase->len, part->mbd.mtd->name);
                kfree(erase);
        }
 
@@ -587,7 +586,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr)
        int block, offset, rc;
        u_long addr;
        size_t retlen;
-       u16 del = const_cpu_to_le16(SECTOR_DELETED);
+       u16 del = cpu_to_le16(SECTOR_DELETED);
 
        block = old_addr / part->block_size;
        offset = (old_addr % part->block_size) / SECTOR_SIZE -
@@ -763,7 +762,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
        struct partition *part;
 
-       if (mtd->type != MTD_NORFLASH)
+       if (mtd->type != MTD_NORFLASH || mtd->size > UINT_MAX)
                return;
 
        part = kzalloc(sizeof(struct partition), GFP_KERNEL);
index 33a5d6ed6f18aa66181feb4ac84774343e68d5d3..3f67e00d98e062a95a3c844e96afd04a066e96c6 100644 (file)
@@ -294,7 +294,8 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        int cis_sector;
 
        /* Check for small page NAND flash */
-       if (mtd->type != MTD_NANDFLASH || mtd->oobsize != OOB_SIZE)
+       if (mtd->type != MTD_NANDFLASH || mtd->oobsize != OOB_SIZE ||
+           mtd->size > UINT_MAX)
                return;
 
        /* Check for SSDFC format by reading CIS/IDI sector */
@@ -316,7 +317,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 
        ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT);
        ssfdc->erase_size = mtd->erasesize;
-       ssfdc->map_len = mtd->size / mtd->erasesize;
+       ssfdc->map_len = (u32)mtd->size / mtd->erasesize;
 
        DEBUG(MTD_DEBUG_LEVEL1,
                "SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n",
@@ -327,7 +328,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        ssfdc->heads = 16;
        ssfdc->sectors = 32;
        get_chs(mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors);
-       ssfdc->cylinders = (unsigned short)((mtd->size >> SECTOR_SHIFT) /
+       ssfdc->cylinders = (unsigned short)(((u32)mtd->size >> SECTOR_SHIFT) /
                        ((long)ssfdc->sectors * (long)ssfdc->heads));
 
        DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile
new file mode 100644 (file)
index 0000000..c1d5013
--- /dev/null
@@ -0,0 +1,7 @@
+obj-$(CONFIG_MTD_TESTS) += mtd_oobtest.o
+obj-$(CONFIG_MTD_TESTS) += mtd_pagetest.o
+obj-$(CONFIG_MTD_TESTS) += mtd_readtest.o
+obj-$(CONFIG_MTD_TESTS) += mtd_speedtest.o
+obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o
+obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
+obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c
new file mode 100644 (file)
index 0000000..afbc3f8
--- /dev/null
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test OOB read and write on MTD device.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#include <asm/div64.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sched.h>
+
+#define PRINT_PREF KERN_INFO "mtd_oobtest: "
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *readbuf;
+static unsigned char *writebuf;
+static unsigned char *bbt;
+
+static int ebcnt;
+static int pgcnt;
+static int errcnt;
+static int use_offset;
+static int use_len;
+static int use_len_max;
+static int vary_offset;
+static unsigned long next = 1;
+
+static inline unsigned int simple_rand(void)
+{
+       next = next * 1103515245 + 12345;
+       return (unsigned int)((next / 65536) % 32768);
+}
+
+static inline void simple_srand(unsigned long seed)
+{
+       next = seed;
+}
+
+static void set_random_data(unsigned char *buf, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; ++i)
+               buf[i] = simple_rand();
+}
+
+static int erase_eraseblock(int ebnum)
+{
+       int err;
+       struct erase_info ei;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       memset(&ei, 0, sizeof(struct erase_info));
+       ei.mtd  = mtd;
+       ei.addr = addr;
+       ei.len  = mtd->erasesize;
+
+       err = mtd->erase(mtd, &ei);
+       if (err) {
+               printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
+               return err;
+       }
+
+       if (ei.state == MTD_ERASE_FAILED) {
+               printk(PRINT_PREF "some erase error occurred at EB %d\n",
+                      ebnum);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int erase_whole_device(void)
+{
+       int err;
+       unsigned int i;
+
+       printk(PRINT_PREF "erasing whole device\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = erase_eraseblock(i);
+               if (err)
+                       return err;
+               cond_resched();
+       }
+       printk(PRINT_PREF "erased %u eraseblocks\n", i);
+       return 0;
+}
+
+static void do_vary_offset(void)
+{
+       use_len -= 1;
+       if (use_len < 1) {
+               use_offset += 1;
+               if (use_offset >= use_len_max)
+                       use_offset = 0;
+               use_len = use_len_max - use_offset;
+       }
+}
+
+static int write_eraseblock(int ebnum)
+{
+       int i;
+       struct mtd_oob_ops ops;
+       int err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
+               set_random_data(writebuf, use_len);
+               ops.mode      = MTD_OOB_AUTO;
+               ops.len       = 0;
+               ops.retlen    = 0;
+               ops.ooblen    = use_len;
+               ops.oobretlen = 0;
+               ops.ooboffs   = use_offset;
+               ops.datbuf    = 0;
+               ops.oobbuf    = writebuf;
+               err = mtd->write_oob(mtd, addr, &ops);
+               if (err || ops.oobretlen != use_len) {
+                       printk(PRINT_PREF "error: writeoob failed at %#llx\n",
+                              (long long)addr);
+                       printk(PRINT_PREF "error: use_len %d, use_offset %d\n",
+                              use_len, use_offset);
+                       errcnt += 1;
+                       return err ? err : -1;
+               }
+               if (vary_offset)
+                       do_vary_offset();
+       }
+
+       return err;
+}
+
+static int write_whole_device(void)
+{
+       int err;
+       unsigned int i;
+
+       printk(PRINT_PREF "writing OOBs of whole device\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = write_eraseblock(i);
+               if (err)
+                       return err;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "written up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "written %u eraseblocks\n", i);
+       return 0;
+}
+
+static int verify_eraseblock(int ebnum)
+{
+       int i;
+       struct mtd_oob_ops ops;
+       int err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
+               set_random_data(writebuf, use_len);
+               ops.mode      = MTD_OOB_AUTO;
+               ops.len       = 0;
+               ops.retlen    = 0;
+               ops.ooblen    = use_len;
+               ops.oobretlen = 0;
+               ops.ooboffs   = use_offset;
+               ops.datbuf    = 0;
+               ops.oobbuf    = readbuf;
+               err = mtd->read_oob(mtd, addr, &ops);
+               if (err || ops.oobretlen != use_len) {
+                       printk(PRINT_PREF "error: readoob failed at %#llx\n",
+                              (long long)addr);
+                       errcnt += 1;
+                       return err ? err : -1;
+               }
+               if (memcmp(readbuf, writebuf, use_len)) {
+                       printk(PRINT_PREF "error: verify failed at %#llx\n",
+                              (long long)addr);
+                       errcnt += 1;
+                       if (errcnt > 1000) {
+                               printk(PRINT_PREF "error: too many errors\n");
+                               return -1;
+                       }
+               }
+               if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
+                       int k;
+
+                       ops.mode      = MTD_OOB_AUTO;
+                       ops.len       = 0;
+                       ops.retlen    = 0;
+                       ops.ooblen    = mtd->ecclayout->oobavail;
+                       ops.oobretlen = 0;
+                       ops.ooboffs   = 0;
+                       ops.datbuf    = 0;
+                       ops.oobbuf    = readbuf;
+                       err = mtd->read_oob(mtd, addr, &ops);
+                       if (err || ops.oobretlen != mtd->ecclayout->oobavail) {
+                               printk(PRINT_PREF "error: readoob failed at "
+                                      "%#llx\n", (long long)addr);
+                               errcnt += 1;
+                               return err ? err : -1;
+                       }
+                       if (memcmp(readbuf + use_offset, writebuf, use_len)) {
+                               printk(PRINT_PREF "error: verify failed at "
+                                      "%#llx\n", (long long)addr);
+                               errcnt += 1;
+                               if (errcnt > 1000) {
+                                       printk(PRINT_PREF "error: too many "
+                                              "errors\n");
+                                       return -1;
+                               }
+                       }
+                       for (k = 0; k < use_offset; ++k)
+                               if (readbuf[k] != 0xff) {
+                                       printk(PRINT_PREF "error: verify 0xff "
+                                              "failed at %#llx\n",
+                                              (long long)addr);
+                                       errcnt += 1;
+                                       if (errcnt > 1000) {
+                                               printk(PRINT_PREF "error: too "
+                                                      "many errors\n");
+                                               return -1;
+                                       }
+                               }
+                       for (k = use_offset + use_len;
+                            k < mtd->ecclayout->oobavail; ++k)
+                               if (readbuf[k] != 0xff) {
+                                       printk(PRINT_PREF "error: verify 0xff "
+                                              "failed at %#llx\n",
+                                              (long long)addr);
+                                       errcnt += 1;
+                                       if (errcnt > 1000) {
+                                               printk(PRINT_PREF "error: too "
+                                                      "many errors\n");
+                                               return -1;
+                                       }
+                               }
+               }
+               if (vary_offset)
+                       do_vary_offset();
+       }
+       return err;
+}
+
+static int verify_eraseblock_in_one_go(int ebnum)
+{
+       struct mtd_oob_ops ops;
+       int err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+       size_t len = mtd->ecclayout->oobavail * pgcnt;
+
+       set_random_data(writebuf, len);
+       ops.mode      = MTD_OOB_AUTO;
+       ops.len       = 0;
+       ops.retlen    = 0;
+       ops.ooblen    = len;
+       ops.oobretlen = 0;
+       ops.ooboffs   = 0;
+       ops.datbuf    = 0;
+       ops.oobbuf    = readbuf;
+       err = mtd->read_oob(mtd, addr, &ops);
+       if (err || ops.oobretlen != len) {
+               printk(PRINT_PREF "error: readoob failed at %#llx\n",
+                      (long long)addr);
+               errcnt += 1;
+               return err ? err : -1;
+       }
+       if (memcmp(readbuf, writebuf, len)) {
+               printk(PRINT_PREF "error: verify failed at %#llx\n",
+                      (long long)addr);
+               errcnt += 1;
+               if (errcnt > 1000) {
+                       printk(PRINT_PREF "error: too many errors\n");
+                       return -1;
+               }
+       }
+
+       return err;
+}
+
+static int verify_all_eraseblocks(void)
+{
+       int err;
+       unsigned int i;
+
+       printk(PRINT_PREF "verifying all eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = verify_eraseblock(i);
+               if (err)
+                       return err;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "verified up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "verified %u eraseblocks\n", i);
+       return 0;
+}
+
+static int is_block_bad(int ebnum)
+{
+       int ret;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       ret = mtd->block_isbad(mtd, addr);
+       if (ret)
+               printk(PRINT_PREF "block %d is bad\n", ebnum);
+       return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+       int i, bad = 0;
+
+       bbt = kmalloc(ebcnt, GFP_KERNEL);
+       if (!bbt) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               return -ENOMEM;
+       }
+       memset(bbt, 0 , ebcnt);
+
+       printk(PRINT_PREF "scanning for bad eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               bbt[i] = is_block_bad(i) ? 1 : 0;
+               if (bbt[i])
+                       bad += 1;
+               cond_resched();
+       }
+       printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
+       return 0;
+}
+
+static int __init mtd_oobtest_init(void)
+{
+       int err = 0;
+       unsigned int i;
+       uint64_t tmp;
+       struct mtd_oob_ops ops;
+       loff_t addr = 0, addr0;
+
+       printk(KERN_INFO "\n");
+       printk(KERN_INFO "=================================================\n");
+       printk(PRINT_PREF "MTD device: %d\n", dev);
+
+       mtd = get_mtd_device(NULL, dev);
+       if (IS_ERR(mtd)) {
+               err = PTR_ERR(mtd);
+               printk(PRINT_PREF "error: cannot get MTD device\n");
+               return err;
+       }
+
+       if (mtd->type != MTD_NANDFLASH) {
+               printk(PRINT_PREF "this test requires NAND flash\n");
+               goto out;
+       }
+
+       tmp = mtd->size;
+       do_div(tmp, mtd->erasesize);
+       ebcnt = tmp;
+       pgcnt = mtd->erasesize / mtd->writesize;
+
+       printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
+              "page size %u, count of eraseblocks %u, pages per "
+              "eraseblock %u, OOB size %u\n",
+              (unsigned long long)mtd->size, mtd->erasesize,
+              mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
+
+       err = -ENOMEM;
+       mtd->erasesize = mtd->erasesize;
+       readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!readbuf) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+       writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!writebuf) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+
+       err = scan_for_bad_eraseblocks();
+       if (err)
+               goto out;
+
+       use_offset = 0;
+       use_len = mtd->ecclayout->oobavail;
+       use_len_max = mtd->ecclayout->oobavail;
+       vary_offset = 0;
+
+       /* First test: write all OOB, read it back and verify */
+       printk(PRINT_PREF "test 1 of 5\n");
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       simple_srand(1);
+       err = write_whole_device();
+       if (err)
+               goto out;
+
+       simple_srand(1);
+       err = verify_all_eraseblocks();
+       if (err)
+               goto out;
+
+       /*
+        * Second test: write all OOB, a block at a time, read it back and
+        * verify.
+        */
+       printk(PRINT_PREF "test 2 of 5\n");
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       simple_srand(3);
+       err = write_whole_device();
+       if (err)
+               goto out;
+
+       /* Check all eraseblocks */
+       simple_srand(3);
+       printk(PRINT_PREF "verifying all eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = verify_eraseblock_in_one_go(i);
+               if (err)
+                       goto out;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "verified up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "verified %u eraseblocks\n", i);
+
+       /*
+        * Third test: write OOB at varying offsets and lengths, read it back
+        * and verify.
+        */
+       printk(PRINT_PREF "test 3 of 5\n");
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       /* Write all eraseblocks */
+       use_offset = 0;
+       use_len = mtd->ecclayout->oobavail;
+       use_len_max = mtd->ecclayout->oobavail;
+       vary_offset = 1;
+       simple_srand(5);
+       printk(PRINT_PREF "writing OOBs of whole device\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = write_eraseblock(i);
+               if (err)
+                       goto out;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "written up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "written %u eraseblocks\n", i);
+
+       /* Check all eraseblocks */
+       use_offset = 0;
+       use_len = mtd->ecclayout->oobavail;
+       use_len_max = mtd->ecclayout->oobavail;
+       vary_offset = 1;
+       simple_srand(5);
+       err = verify_all_eraseblocks();
+       if (err)
+               goto out;
+
+       use_offset = 0;
+       use_len = mtd->ecclayout->oobavail;
+       use_len_max = mtd->ecclayout->oobavail;
+       vary_offset = 0;
+
+       /* Fourth test: try to write off end of device */
+       printk(PRINT_PREF "test 4 of 5\n");
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       addr0 = 0;
+       for (i = 0; bbt[i] && i < ebcnt; ++i)
+               addr0 += mtd->erasesize;
+
+       /* Attempt to write off end of OOB */
+       ops.mode      = MTD_OOB_AUTO;
+       ops.len       = 0;
+       ops.retlen    = 0;
+       ops.ooblen    = 1;
+       ops.oobretlen = 0;
+       ops.ooboffs   = mtd->ecclayout->oobavail;
+       ops.datbuf    = 0;
+       ops.oobbuf    = writebuf;
+       printk(PRINT_PREF "attempting to start write past end of OOB\n");
+       printk(PRINT_PREF "an error is expected...\n");
+       err = mtd->write_oob(mtd, addr0, &ops);
+       if (err) {
+               printk(PRINT_PREF "error occurred as expected\n");
+               err = 0;
+       } else {
+               printk(PRINT_PREF "error: can write past end of OOB\n");
+               errcnt += 1;
+       }
+
+       /* Attempt to read off end of OOB */
+       ops.mode      = MTD_OOB_AUTO;
+       ops.len       = 0;
+       ops.retlen    = 0;
+       ops.ooblen    = 1;
+       ops.oobretlen = 0;
+       ops.ooboffs   = mtd->ecclayout->oobavail;
+       ops.datbuf    = 0;
+       ops.oobbuf    = readbuf;
+       printk(PRINT_PREF "attempting to start read past end of OOB\n");
+       printk(PRINT_PREF "an error is expected...\n");
+       err = mtd->read_oob(mtd, addr0, &ops);
+       if (err) {
+               printk(PRINT_PREF "error occurred as expected\n");
+               err = 0;
+       } else {
+               printk(PRINT_PREF "error: can read past end of OOB\n");
+               errcnt += 1;
+       }
+
+       if (bbt[ebcnt - 1])
+               printk(PRINT_PREF "skipping end of device tests because last "
+                      "block is bad\n");
+       else {
+               /* Attempt to write off end of device */
+               ops.mode      = MTD_OOB_AUTO;
+               ops.len       = 0;
+               ops.retlen    = 0;
+               ops.ooblen    = mtd->ecclayout->oobavail + 1;
+               ops.oobretlen = 0;
+               ops.ooboffs   = 0;
+               ops.datbuf    = 0;
+               ops.oobbuf    = writebuf;
+               printk(PRINT_PREF "attempting to write past end of device\n");
+               printk(PRINT_PREF "an error is expected...\n");
+               err = mtd->write_oob(mtd, mtd->size - mtd->writesize, &ops);
+               if (err) {
+                       printk(PRINT_PREF "error occurred as expected\n");
+                       err = 0;
+               } else {
+                       printk(PRINT_PREF "error: wrote past end of device\n");
+                       errcnt += 1;
+               }
+
+               /* Attempt to read off end of device */
+               ops.mode      = MTD_OOB_AUTO;
+               ops.len       = 0;
+               ops.retlen    = 0;
+               ops.ooblen    = mtd->ecclayout->oobavail + 1;
+               ops.oobretlen = 0;
+               ops.ooboffs   = 0;
+               ops.datbuf    = 0;
+               ops.oobbuf    = readbuf;
+               printk(PRINT_PREF "attempting to read past end of device\n");
+               printk(PRINT_PREF "an error is expected...\n");
+               err = mtd->read_oob(mtd, mtd->size - mtd->writesize, &ops);
+               if (err) {
+                       printk(PRINT_PREF "error occurred as expected\n");
+                       err = 0;
+               } else {
+                       printk(PRINT_PREF "error: read past end of device\n");
+                       errcnt += 1;
+               }
+
+               err = erase_eraseblock(ebcnt - 1);
+               if (err)
+                       goto out;
+
+               /* Attempt to write off end of device */
+               ops.mode      = MTD_OOB_AUTO;
+               ops.len       = 0;
+               ops.retlen    = 0;
+               ops.ooblen    = mtd->ecclayout->oobavail;
+               ops.oobretlen = 0;
+               ops.ooboffs   = 1;
+               ops.datbuf    = 0;
+               ops.oobbuf    = writebuf;
+               printk(PRINT_PREF "attempting to write past end of device\n");
+               printk(PRINT_PREF "an error is expected...\n");
+               err = mtd->write_oob(mtd, mtd->size - mtd->writesize, &ops);
+               if (err) {
+                       printk(PRINT_PREF "error occurred as expected\n");
+                       err = 0;
+               } else {
+                       printk(PRINT_PREF "error: wrote past end of device\n");
+                       errcnt += 1;
+               }
+
+               /* Attempt to read off end of device */
+               ops.mode      = MTD_OOB_AUTO;
+               ops.len       = 0;
+               ops.retlen    = 0;
+               ops.ooblen    = mtd->ecclayout->oobavail;
+               ops.oobretlen = 0;
+               ops.ooboffs   = 1;
+               ops.datbuf    = 0;
+               ops.oobbuf    = readbuf;
+               printk(PRINT_PREF "attempting to read past end of device\n");
+               printk(PRINT_PREF "an error is expected...\n");
+               err = mtd->read_oob(mtd, mtd->size - mtd->writesize, &ops);
+               if (err) {
+                       printk(PRINT_PREF "error occurred as expected\n");
+                       err = 0;
+               } else {
+                       printk(PRINT_PREF "error: read past end of device\n");
+                       errcnt += 1;
+               }
+       }
+
+       /* Fifth test: write / read across block boundaries */
+       printk(PRINT_PREF "test 5 of 5\n");
+
+       /* Erase all eraseblocks */
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       /* Write all eraseblocks */
+       simple_srand(11);
+       printk(PRINT_PREF "writing OOBs of whole device\n");
+       for (i = 0; i < ebcnt - 1; ++i) {
+               int cnt = 2;
+               int pg;
+               size_t sz = mtd->ecclayout->oobavail;
+               if (bbt[i] || bbt[i + 1])
+                       continue;
+               addr = (i + 1) * mtd->erasesize - mtd->writesize;
+               for (pg = 0; pg < cnt; ++pg) {
+                       set_random_data(writebuf, sz);
+                       ops.mode      = MTD_OOB_AUTO;
+                       ops.len       = 0;
+                       ops.retlen    = 0;
+                       ops.ooblen    = sz;
+                       ops.oobretlen = 0;
+                       ops.ooboffs   = 0;
+                       ops.datbuf    = 0;
+                       ops.oobbuf    = writebuf;
+                       err = mtd->write_oob(mtd, addr, &ops);
+                       if (err)
+                               goto out;
+                       if (i % 256 == 0)
+                               printk(PRINT_PREF "written up to eraseblock "
+                                      "%u\n", i);
+                       cond_resched();
+                       addr += mtd->writesize;
+               }
+       }
+       printk(PRINT_PREF "written %u eraseblocks\n", i);
+
+       /* Check all eraseblocks */
+       simple_srand(11);
+       printk(PRINT_PREF "verifying all eraseblocks\n");
+       for (i = 0; i < ebcnt - 1; ++i) {
+               if (bbt[i] || bbt[i + 1])
+                       continue;
+               set_random_data(writebuf, mtd->ecclayout->oobavail * 2);
+               addr = (i + 1) * mtd->erasesize - mtd->writesize;
+               ops.mode      = MTD_OOB_AUTO;
+               ops.len       = 0;
+               ops.retlen    = 0;
+               ops.ooblen    = mtd->ecclayout->oobavail * 2;
+               ops.oobretlen = 0;
+               ops.ooboffs   = 0;
+               ops.datbuf    = 0;
+               ops.oobbuf    = readbuf;
+               err = mtd->read_oob(mtd, addr, &ops);
+               if (err)
+                       goto out;
+               if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) {
+                       printk(PRINT_PREF "error: verify failed at %#llx\n",
+                              (long long)addr);
+                       errcnt += 1;
+                       if (errcnt > 1000) {
+                               printk(PRINT_PREF "error: too many errors\n");
+                               goto out;
+                       }
+               }
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "verified up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "verified %u eraseblocks\n", i);
+
+       printk(PRINT_PREF "finished with %d errors\n", errcnt);
+out:
+       kfree(bbt);
+       kfree(writebuf);
+       kfree(readbuf);
+       put_mtd_device(mtd);
+       if (err)
+               printk(PRINT_PREF "error %d occurred\n", err);
+       printk(KERN_INFO "=================================================\n");
+       return err;
+}
+module_init(mtd_oobtest_init);
+
+static void __exit mtd_oobtest_exit(void)
+{
+       return;
+}
+module_exit(mtd_oobtest_exit);
+
+MODULE_DESCRIPTION("Out-of-band test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c
new file mode 100644 (file)
index 0000000..9648818
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test page read and write on MTD device.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#include <asm/div64.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sched.h>
+
+#define PRINT_PREF KERN_INFO "mtd_pagetest: "
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *twopages;
+static unsigned char *writebuf;
+static unsigned char *boundary;
+static unsigned char *bbt;
+
+static int pgsize;
+static int bufsize;
+static int ebcnt;
+static int pgcnt;
+static int errcnt;
+static unsigned long next = 1;
+
+static inline unsigned int simple_rand(void)
+{
+       next = next * 1103515245 + 12345;
+       return (unsigned int)((next / 65536) % 32768);
+}
+
+static inline void simple_srand(unsigned long seed)
+{
+       next = seed;
+}
+
+static void set_random_data(unsigned char *buf, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; ++i)
+               buf[i] = simple_rand();
+}
+
+static int erase_eraseblock(int ebnum)
+{
+       int err;
+       struct erase_info ei;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       memset(&ei, 0, sizeof(struct erase_info));
+       ei.mtd  = mtd;
+       ei.addr = addr;
+       ei.len  = mtd->erasesize;
+
+       err = mtd->erase(mtd, &ei);
+       if (err) {
+               printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
+               return err;
+       }
+
+       if (ei.state == MTD_ERASE_FAILED) {
+               printk(PRINT_PREF "some erase error occurred at EB %d\n",
+                      ebnum);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int write_eraseblock(int ebnum)
+{
+       int err = 0;
+       size_t written = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       set_random_data(writebuf, mtd->erasesize);
+       cond_resched();
+       err = mtd->write(mtd, addr, mtd->erasesize, &written, writebuf);
+       if (err || written != mtd->erasesize)
+               printk(PRINT_PREF "error: write failed at %#llx\n",
+                      (long long)addr);
+
+       return err;
+}
+
+static int verify_eraseblock(int ebnum)
+{
+       uint32_t j;
+       size_t read = 0;
+       int err = 0, i;
+       loff_t addr0, addrn;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       addr0 = 0;
+       for (i = 0; bbt[i] && i < ebcnt; ++i)
+               addr0 += mtd->erasesize;
+
+       addrn = mtd->size;
+       for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i)
+               addrn -= mtd->erasesize;
+
+       set_random_data(writebuf, mtd->erasesize);
+       for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
+               /* Do a read to set the internal dataRAMs to different data */
+               err = mtd->read(mtd, addr0, bufsize, &read, twopages);
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != bufsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)addr0);
+                       return err;
+               }
+               err = mtd->read(mtd, addrn - bufsize, bufsize, &read, twopages);
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != bufsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)(addrn - bufsize));
+                       return err;
+               }
+               memset(twopages, 0, bufsize);
+               read = 0;
+               err = mtd->read(mtd, addr, bufsize, &read, twopages);
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != bufsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)addr);
+                       break;
+               }
+               if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
+                       printk(PRINT_PREF "error: verify failed at %#llx\n",
+                              (long long)addr);
+                       errcnt += 1;
+               }
+       }
+       /* Check boundary between eraseblocks */
+       if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) {
+               unsigned long oldnext = next;
+               /* Do a read to set the internal dataRAMs to different data */
+               err = mtd->read(mtd, addr0, bufsize, &read, twopages);
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != bufsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)addr0);
+                       return err;
+               }
+               err = mtd->read(mtd, addrn - bufsize, bufsize, &read, twopages);
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != bufsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)(addrn - bufsize));
+                       return err;
+               }
+               memset(twopages, 0, bufsize);
+               read = 0;
+               err = mtd->read(mtd, addr, bufsize, &read, twopages);
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != bufsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)addr);
+                       return err;
+               }
+               memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
+               set_random_data(boundary + pgsize, pgsize);
+               if (memcmp(twopages, boundary, bufsize)) {
+                       printk(PRINT_PREF "error: verify failed at %#llx\n",
+                              (long long)addr);
+                       errcnt += 1;
+               }
+               next = oldnext;
+       }
+       return err;
+}
+
+static int crosstest(void)
+{
+       size_t read = 0;
+       int err = 0, i;
+       loff_t addr, addr0, addrn;
+       unsigned char *pp1, *pp2, *pp3, *pp4;
+
+       printk(PRINT_PREF "crosstest\n");
+       pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
+       if (!pp1) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               return -ENOMEM;
+       }
+       pp2 = pp1 + pgsize;
+       pp3 = pp2 + pgsize;
+       pp4 = pp3 + pgsize;
+       memset(pp1, 0, pgsize * 4);
+
+       addr0 = 0;
+       for (i = 0; bbt[i] && i < ebcnt; ++i)
+               addr0 += mtd->erasesize;
+
+       addrn = mtd->size;
+       for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i)
+               addrn -= mtd->erasesize;
+
+       /* Read 2nd-to-last page to pp1 */
+       read = 0;
+       addr = addrn - pgsize - pgsize;
+       err = mtd->read(mtd, addr, pgsize, &read, pp1);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != pgsize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n",
+                      (long long)addr);
+               kfree(pp1);
+               return err;
+       }
+
+       /* Read 3rd-to-last page to pp1 */
+       read = 0;
+       addr = addrn - pgsize - pgsize - pgsize;
+       err = mtd->read(mtd, addr, pgsize, &read, pp1);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != pgsize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n",
+                      (long long)addr);
+               kfree(pp1);
+               return err;
+       }
+
+       /* Read first page to pp2 */
+       read = 0;
+       addr = addr0;
+       printk(PRINT_PREF "reading page at %#llx\n", (long long)addr);
+       err = mtd->read(mtd, addr, pgsize, &read, pp2);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != pgsize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n",
+                      (long long)addr);
+               kfree(pp1);
+               return err;
+       }
+
+       /* Read last page to pp3 */
+       read = 0;
+       addr = addrn - pgsize;
+       printk(PRINT_PREF "reading page at %#llx\n", (long long)addr);
+       err = mtd->read(mtd, addr, pgsize, &read, pp3);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != pgsize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n",
+                      (long long)addr);
+               kfree(pp1);
+               return err;
+       }
+
+       /* Read first page again to pp4 */
+       read = 0;
+       addr = addr0;
+       printk(PRINT_PREF "reading page at %#llx\n", (long long)addr);
+       err = mtd->read(mtd, addr, pgsize, &read, pp4);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != pgsize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n",
+                      (long long)addr);
+               kfree(pp1);
+               return err;
+       }
+
+       /* pp2 and pp4 should be the same */
+       printk(PRINT_PREF "verifying pages read at %#llx match\n",
+              (long long)addr0);
+       if (memcmp(pp2, pp4, pgsize)) {
+               printk(PRINT_PREF "verify failed!\n");
+               errcnt += 1;
+       } else if (!err)
+               printk(PRINT_PREF "crosstest ok\n");
+       kfree(pp1);
+       return err;
+}
+
+static int erasecrosstest(void)
+{
+       size_t read = 0, written = 0;
+       int err = 0, i, ebnum, ok = 1, ebnum2;
+       loff_t addr0;
+       char *readbuf = twopages;
+
+       printk(PRINT_PREF "erasecrosstest\n");
+
+       ebnum = 0;
+       addr0 = 0;
+       for (i = 0; bbt[i] && i < ebcnt; ++i) {
+               addr0 += mtd->erasesize;
+               ebnum += 1;
+       }
+
+       ebnum2 = ebcnt - 1;
+       while (ebnum2 && bbt[ebnum2])
+               ebnum2 -= 1;
+
+       printk(PRINT_PREF "erasing block %d\n", ebnum);
+       err = erase_eraseblock(ebnum);
+       if (err)
+               return err;
+
+       printk(PRINT_PREF "writing 1st page of block %d\n", ebnum);
+       set_random_data(writebuf, pgsize);
+       strcpy(writebuf, "There is no data like this!");
+       err = mtd->write(mtd, addr0, pgsize, &written, writebuf);
+       if (err || written != pgsize) {
+               printk(PRINT_PREF "error: write failed at %#llx\n",
+                      (long long)addr0);
+               return err ? err : -1;
+       }
+
+       printk(PRINT_PREF "reading 1st page of block %d\n", ebnum);
+       memset(readbuf, 0, pgsize);
+       err = mtd->read(mtd, addr0, pgsize, &read, readbuf);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != pgsize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n",
+                      (long long)addr0);
+               return err ? err : -1;
+       }
+
+       printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum);
+       if (memcmp(writebuf, readbuf, pgsize)) {
+               printk(PRINT_PREF "verify failed!\n");
+               errcnt += 1;
+               ok = 0;
+               return err;
+       }
+
+       printk(PRINT_PREF "erasing block %d\n", ebnum);
+       err = erase_eraseblock(ebnum);
+       if (err)
+               return err;
+
+       printk(PRINT_PREF "writing 1st page of block %d\n", ebnum);
+       set_random_data(writebuf, pgsize);
+       strcpy(writebuf, "There is no data like this!");
+       err = mtd->write(mtd, addr0, pgsize, &written, writebuf);
+       if (err || written != pgsize) {
+               printk(PRINT_PREF "error: write failed at %#llx\n",
+                      (long long)addr0);
+               return err ? err : -1;
+       }
+
+       printk(PRINT_PREF "erasing block %d\n", ebnum2);
+       err = erase_eraseblock(ebnum2);
+       if (err)
+               return err;
+
+       printk(PRINT_PREF "reading 1st page of block %d\n", ebnum);
+       memset(readbuf, 0, pgsize);
+       err = mtd->read(mtd, addr0, pgsize, &read, readbuf);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != pgsize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n",
+                      (long long)addr0);
+               return err ? err : -1;
+       }
+
+       printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum);
+       if (memcmp(writebuf, readbuf, pgsize)) {
+               printk(PRINT_PREF "verify failed!\n");
+               errcnt += 1;
+               ok = 0;
+       }
+
+       if (ok && !err)
+               printk(PRINT_PREF "erasecrosstest ok\n");
+       return err;
+}
+
+static int erasetest(void)
+{
+       size_t read = 0, written = 0;
+       int err = 0, i, ebnum, ok = 1;
+       loff_t addr0;
+
+       printk(PRINT_PREF "erasetest\n");
+
+       ebnum = 0;
+       addr0 = 0;
+       for (i = 0; bbt[i] && i < ebcnt; ++i) {
+               addr0 += mtd->erasesize;
+               ebnum += 1;
+       }
+
+       printk(PRINT_PREF "erasing block %d\n", ebnum);
+       err = erase_eraseblock(ebnum);
+       if (err)
+               return err;
+
+       printk(PRINT_PREF "writing 1st page of block %d\n", ebnum);
+       set_random_data(writebuf, pgsize);
+       err = mtd->write(mtd, addr0, pgsize, &written, writebuf);
+       if (err || written != pgsize) {
+               printk(PRINT_PREF "error: write failed at %#llx\n",
+                      (long long)addr0);
+               return err ? err : -1;
+       }
+
+       printk(PRINT_PREF "erasing block %d\n", ebnum);
+       err = erase_eraseblock(ebnum);
+       if (err)
+               return err;
+
+       printk(PRINT_PREF "reading 1st page of block %d\n", ebnum);
+       err = mtd->read(mtd, addr0, pgsize, &read, twopages);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != pgsize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n",
+                      (long long)addr0);
+               return err ? err : -1;
+       }
+
+       printk(PRINT_PREF "verifying 1st page of block %d is all 0xff\n",
+              ebnum);
+       for (i = 0; i < pgsize; ++i)
+               if (twopages[i] != 0xff) {
+                       printk(PRINT_PREF "verifying all 0xff failed at %d\n",
+                              i);
+                       errcnt += 1;
+                       ok = 0;
+                       break;
+               }
+
+       if (ok && !err)
+               printk(PRINT_PREF "erasetest ok\n");
+
+       return err;
+}
+
+static int is_block_bad(int ebnum)
+{
+       loff_t addr = ebnum * mtd->erasesize;
+       int ret;
+
+       ret = mtd->block_isbad(mtd, addr);
+       if (ret)
+               printk(PRINT_PREF "block %d is bad\n", ebnum);
+       return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+       int i, bad = 0;
+
+       bbt = kmalloc(ebcnt, GFP_KERNEL);
+       if (!bbt) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               return -ENOMEM;
+       }
+       memset(bbt, 0 , ebcnt);
+
+       printk(PRINT_PREF "scanning for bad eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               bbt[i] = is_block_bad(i) ? 1 : 0;
+               if (bbt[i])
+                       bad += 1;
+               cond_resched();
+       }
+       printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
+       return 0;
+}
+
+static int __init mtd_pagetest_init(void)
+{
+       int err = 0;
+       uint64_t tmp;
+       uint32_t i;
+
+       printk(KERN_INFO "\n");
+       printk(KERN_INFO "=================================================\n");
+       printk(PRINT_PREF "MTD device: %d\n", dev);
+
+       mtd = get_mtd_device(NULL, dev);
+       if (IS_ERR(mtd)) {
+               err = PTR_ERR(mtd);
+               printk(PRINT_PREF "error: cannot get MTD device\n");
+               return err;
+       }
+
+       if (mtd->type != MTD_NANDFLASH) {
+               printk(PRINT_PREF "this test requires NAND flash\n");
+               goto out;
+       }
+
+       tmp = mtd->size;
+       do_div(tmp, mtd->erasesize);
+       ebcnt = tmp;
+       pgcnt = mtd->erasesize / mtd->writesize;
+
+       printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
+              "page size %u, count of eraseblocks %u, pages per "
+              "eraseblock %u, OOB size %u\n",
+              (unsigned long long)mtd->size, mtd->erasesize,
+              pgsize, ebcnt, pgcnt, mtd->oobsize);
+
+       err = -ENOMEM;
+       bufsize = pgsize * 2;
+       writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!writebuf) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+       twopages = kmalloc(bufsize, GFP_KERNEL);
+       if (!twopages) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+       boundary = kmalloc(bufsize, GFP_KERNEL);
+       if (!boundary) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+
+       err = scan_for_bad_eraseblocks();
+       if (err)
+               goto out;
+
+       /* Erase all eraseblocks */
+       printk(PRINT_PREF "erasing whole device\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = erase_eraseblock(i);
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       printk(PRINT_PREF "erased %u eraseblocks\n", i);
+
+       /* Write all eraseblocks */
+       simple_srand(1);
+       printk(PRINT_PREF "writing whole device\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = write_eraseblock(i);
+               if (err)
+                       goto out;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "written up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "written %u eraseblocks\n", i);
+
+       /* Check all eraseblocks */
+       simple_srand(1);
+       printk(PRINT_PREF "verifying all eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = verify_eraseblock(i);
+               if (err)
+                       goto out;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "verified up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "verified %u eraseblocks\n", i);
+
+       err = crosstest();
+       if (err)
+               goto out;
+
+       err = erasecrosstest();
+       if (err)
+               goto out;
+
+       err = erasetest();
+       if (err)
+               goto out;
+
+       printk(PRINT_PREF "finished with %d errors\n", errcnt);
+out:
+
+       kfree(bbt);
+       kfree(boundary);
+       kfree(twopages);
+       kfree(writebuf);
+       put_mtd_device(mtd);
+       if (err)
+               printk(PRINT_PREF "error %d occurred\n", err);
+       printk(KERN_INFO "=================================================\n");
+       return err;
+}
+module_init(mtd_pagetest_init);
+
+static void __exit mtd_pagetest_exit(void)
+{
+       return;
+}
+module_exit(mtd_pagetest_exit);
+
+MODULE_DESCRIPTION("NAND page test");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c
new file mode 100644 (file)
index 0000000..645e77f
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Check MTD device read.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sched.h>
+
+#define PRINT_PREF KERN_INFO "mtd_readtest: "
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *iobuf;
+static unsigned char *iobuf1;
+static unsigned char *bbt;
+
+static int pgsize;
+static int ebcnt;
+static int pgcnt;
+
+static int read_eraseblock_by_page(int ebnum)
+{
+       size_t read = 0;
+       int i, ret, err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+       void *buf = iobuf;
+       void *oobbuf = iobuf1;
+
+       for (i = 0; i < pgcnt; i++) {
+               memset(buf, 0 , pgcnt);
+               ret = mtd->read(mtd, addr, pgsize, &read, buf);
+               if (ret == -EUCLEAN)
+                       ret = 0;
+               if (ret || read != pgsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)addr);
+                       if (!err)
+                               err = ret;
+                       if (!err)
+                               err = -EINVAL;
+               }
+               if (mtd->oobsize) {
+                       struct mtd_oob_ops ops;
+
+                       ops.mode      = MTD_OOB_PLACE;
+                       ops.len       = 0;
+                       ops.retlen    = 0;
+                       ops.ooblen    = mtd->oobsize;
+                       ops.oobretlen = 0;
+                       ops.ooboffs   = 0;
+                       ops.datbuf    = 0;
+                       ops.oobbuf    = oobbuf;
+                       ret = mtd->read_oob(mtd, addr, &ops);
+                       if (ret || ops.oobretlen != mtd->oobsize) {
+                               printk(PRINT_PREF "error: read oob failed at "
+                                                 "%#llx\n", (long long)addr);
+                               if (!err)
+                                       err = ret;
+                               if (!err)
+                                       err = -EINVAL;
+                       }
+                       oobbuf += mtd->oobsize;
+               }
+               addr += pgsize;
+               buf += pgsize;
+       }
+
+       return err;
+}
+
+static void dump_eraseblock(int ebnum)
+{
+       int i, j, n;
+       char line[128];
+       int pg, oob;
+
+       printk(PRINT_PREF "dumping eraseblock %d\n", ebnum);
+       n = mtd->erasesize;
+       for (i = 0; i < n;) {
+               char *p = line;
+
+               p += sprintf(p, "%05x: ", i);
+               for (j = 0; j < 32 && i < n; j++, i++)
+                       p += sprintf(p, "%02x", (unsigned int)iobuf[i]);
+               printk(KERN_CRIT "%s\n", line);
+               cond_resched();
+       }
+       if (!mtd->oobsize)
+               return;
+       printk(PRINT_PREF "dumping oob from eraseblock %d\n", ebnum);
+       n = mtd->oobsize;
+       for (pg = 0, i = 0; pg < pgcnt; pg++)
+               for (oob = 0; oob < n;) {
+                       char *p = line;
+
+                       p += sprintf(p, "%05x: ", i);
+                       for (j = 0; j < 32 && oob < n; j++, oob++, i++)
+                               p += sprintf(p, "%02x",
+                                            (unsigned int)iobuf1[i]);
+                       printk(KERN_CRIT "%s\n", line);
+                       cond_resched();
+               }
+}
+
+static int is_block_bad(int ebnum)
+{
+       loff_t addr = ebnum * mtd->erasesize;
+       int ret;
+
+       ret = mtd->block_isbad(mtd, addr);
+       if (ret)
+               printk(PRINT_PREF "block %d is bad\n", ebnum);
+       return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+       int i, bad = 0;
+
+       bbt = kmalloc(ebcnt, GFP_KERNEL);
+       if (!bbt) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               return -ENOMEM;
+       }
+       memset(bbt, 0 , ebcnt);
+
+       printk(PRINT_PREF "scanning for bad eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               bbt[i] = is_block_bad(i) ? 1 : 0;
+               if (bbt[i])
+                       bad += 1;
+               cond_resched();
+       }
+       printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
+       return 0;
+}
+
+static int __init mtd_readtest_init(void)
+{
+       uint64_t tmp;
+       int err, i;
+
+       printk(KERN_INFO "\n");
+       printk(KERN_INFO "=================================================\n");
+       printk(PRINT_PREF "MTD device: %d\n", dev);
+
+       mtd = get_mtd_device(NULL, dev);
+       if (IS_ERR(mtd)) {
+               err = PTR_ERR(mtd);
+               printk(PRINT_PREF "error: Cannot get MTD device\n");
+               return err;
+       }
+
+       if (mtd->writesize == 1) {
+               printk(PRINT_PREF "not NAND flash, assume page size is 512 "
+                      "bytes.\n");
+               pgsize = 512;
+       } else
+               pgsize = mtd->writesize;
+
+       tmp = mtd->size;
+       do_div(tmp, mtd->erasesize);
+       ebcnt = tmp;
+       pgcnt = mtd->erasesize / mtd->writesize;
+
+       printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
+              "page size %u, count of eraseblocks %u, pages per "
+              "eraseblock %u, OOB size %u\n",
+              (unsigned long long)mtd->size, mtd->erasesize,
+              pgsize, ebcnt, pgcnt, mtd->oobsize);
+
+       err = -ENOMEM;
+       iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!iobuf) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+       iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!iobuf1) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+
+       err = scan_for_bad_eraseblocks();
+       if (err)
+               goto out;
+
+       /* Read all eraseblocks 1 page at a time */
+       printk(PRINT_PREF "testing page read\n");
+       for (i = 0; i < ebcnt; ++i) {
+               int ret;
+
+               if (bbt[i])
+                       continue;
+               ret = read_eraseblock_by_page(i);
+               if (ret) {
+                       dump_eraseblock(i);
+                       if (!err)
+                               err = ret;
+               }
+               cond_resched();
+       }
+
+       if (err)
+               printk(PRINT_PREF "finished with errors\n");
+       else
+               printk(PRINT_PREF "finished\n");
+
+out:
+
+       kfree(iobuf);
+       kfree(iobuf1);
+       kfree(bbt);
+       put_mtd_device(mtd);
+       if (err)
+               printk(PRINT_PREF "error %d occurred\n", err);
+       printk(KERN_INFO "=================================================\n");
+       return err;
+}
+module_init(mtd_readtest_init);
+
+static void __exit mtd_readtest_exit(void)
+{
+       return;
+}
+module_exit(mtd_readtest_exit);
+
+MODULE_DESCRIPTION("Read test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c
new file mode 100644 (file)
index 0000000..141363a
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test read and write speed of a MTD device.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sched.h>
+
+#define PRINT_PREF KERN_INFO "mtd_speedtest: "
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *iobuf;
+static unsigned char *bbt;
+
+static int pgsize;
+static int ebcnt;
+static int pgcnt;
+static int goodebcnt;
+static struct timeval start, finish;
+static unsigned long next = 1;
+
+static inline unsigned int simple_rand(void)
+{
+       next = next * 1103515245 + 12345;
+       return (unsigned int)((next / 65536) % 32768);
+}
+
+static inline void simple_srand(unsigned long seed)
+{
+       next = seed;
+}
+
+static void set_random_data(unsigned char *buf, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; ++i)
+               buf[i] = simple_rand();
+}
+
+static int erase_eraseblock(int ebnum)
+{
+       int err;
+       struct erase_info ei;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       memset(&ei, 0, sizeof(struct erase_info));
+       ei.mtd  = mtd;
+       ei.addr = addr;
+       ei.len  = mtd->erasesize;
+
+       err = mtd->erase(mtd, &ei);
+       if (err) {
+               printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
+               return err;
+       }
+
+       if (ei.state == MTD_ERASE_FAILED) {
+               printk(PRINT_PREF "some erase error occurred at EB %d\n",
+                      ebnum);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int erase_whole_device(void)
+{
+       int err;
+       unsigned int i;
+
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = erase_eraseblock(i);
+               if (err)
+                       return err;
+               cond_resched();
+       }
+       return 0;
+}
+
+static int write_eraseblock(int ebnum)
+{
+       size_t written = 0;
+       int err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       err = mtd->write(mtd, addr, mtd->erasesize, &written, iobuf);
+       if (err || written != mtd->erasesize) {
+               printk(PRINT_PREF "error: write failed at %#llx\n", addr);
+               if (!err)
+                       err = -EINVAL;
+       }
+
+       return err;
+}
+
+static int write_eraseblock_by_page(int ebnum)
+{
+       size_t written = 0;
+       int i, err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+       void *buf = iobuf;
+
+       for (i = 0; i < pgcnt; i++) {
+               err = mtd->write(mtd, addr, pgsize, &written, buf);
+               if (err || written != pgsize) {
+                       printk(PRINT_PREF "error: write failed at %#llx\n",
+                              addr);
+                       if (!err)
+                               err = -EINVAL;
+                       break;
+               }
+               addr += pgsize;
+               buf += pgsize;
+       }
+
+       return err;
+}
+
+static int write_eraseblock_by_2pages(int ebnum)
+{
+       size_t written = 0, sz = pgsize * 2;
+       int i, n = pgcnt / 2, err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+       void *buf = iobuf;
+
+       for (i = 0; i < n; i++) {
+               err = mtd->write(mtd, addr, sz, &written, buf);
+               if (err || written != sz) {
+                       printk(PRINT_PREF "error: write failed at %#llx\n",
+                              addr);
+                       if (!err)
+                               err = -EINVAL;
+                       return err;
+               }
+               addr += sz;
+               buf += sz;
+       }
+       if (pgcnt % 2) {
+               err = mtd->write(mtd, addr, pgsize, &written, buf);
+               if (err || written != pgsize) {
+                       printk(PRINT_PREF "error: write failed at %#llx\n",
+                              addr);
+                       if (!err)
+                               err = -EINVAL;
+               }
+       }
+
+       return err;
+}
+
+static int read_eraseblock(int ebnum)
+{
+       size_t read = 0;
+       int err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       err = mtd->read(mtd, addr, mtd->erasesize, &read, iobuf);
+       /* Ignore corrected ECC errors */
+       if (err == -EUCLEAN)
+               err = 0;
+       if (err || read != mtd->erasesize) {
+               printk(PRINT_PREF "error: read failed at %#llx\n", addr);
+               if (!err)
+                       err = -EINVAL;
+       }
+
+       return err;
+}
+
+static int read_eraseblock_by_page(int ebnum)
+{
+       size_t read = 0;
+       int i, err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+       void *buf = iobuf;
+
+       for (i = 0; i < pgcnt; i++) {
+               err = mtd->read(mtd, addr, pgsize, &read, buf);
+               /* Ignore corrected ECC errors */
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != pgsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              addr);
+                       if (!err)
+                               err = -EINVAL;
+                       break;
+               }
+               addr += pgsize;
+               buf += pgsize;
+       }
+
+       return err;
+}
+
+static int read_eraseblock_by_2pages(int ebnum)
+{
+       size_t read = 0, sz = pgsize * 2;
+       int i, n = pgcnt / 2, err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+       void *buf = iobuf;
+
+       for (i = 0; i < n; i++) {
+               err = mtd->read(mtd, addr, sz, &read, buf);
+               /* Ignore corrected ECC errors */
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != sz) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              addr);
+                       if (!err)
+                               err = -EINVAL;
+                       return err;
+               }
+               addr += sz;
+               buf += sz;
+       }
+       if (pgcnt % 2) {
+               err = mtd->read(mtd, addr, pgsize, &read, buf);
+               /* Ignore corrected ECC errors */
+               if (err == -EUCLEAN)
+                       err = 0;
+               if (err || read != pgsize) {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              addr);
+                       if (!err)
+                               err = -EINVAL;
+               }
+       }
+
+       return err;
+}
+
+static int is_block_bad(int ebnum)
+{
+       loff_t addr = ebnum * mtd->erasesize;
+       int ret;
+
+       ret = mtd->block_isbad(mtd, addr);
+       if (ret)
+               printk(PRINT_PREF "block %d is bad\n", ebnum);
+       return ret;
+}
+
+static inline void start_timing(void)
+{
+       do_gettimeofday(&start);
+}
+
+static inline void stop_timing(void)
+{
+       do_gettimeofday(&finish);
+}
+
+static long calc_speed(void)
+{
+       long ms, k, speed;
+
+       ms = (finish.tv_sec - start.tv_sec) * 1000 +
+            (finish.tv_usec - start.tv_usec) / 1000;
+       k = goodebcnt * mtd->erasesize / 1024;
+       speed = (k * 1000) / ms;
+       return speed;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+       int i, bad = 0;
+
+       bbt = kmalloc(ebcnt, GFP_KERNEL);
+       if (!bbt) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               return -ENOMEM;
+       }
+       memset(bbt, 0 , ebcnt);
+
+       printk(PRINT_PREF "scanning for bad eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               bbt[i] = is_block_bad(i) ? 1 : 0;
+               if (bbt[i])
+                       bad += 1;
+               cond_resched();
+       }
+       printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
+       goodebcnt = ebcnt - bad;
+       return 0;
+}
+
+static int __init mtd_speedtest_init(void)
+{
+       int err, i;
+       long speed;
+       uint64_t tmp;
+
+       printk(KERN_INFO "\n");
+       printk(KERN_INFO "=================================================\n");
+       printk(PRINT_PREF "MTD device: %d\n", dev);
+
+       mtd = get_mtd_device(NULL, dev);
+       if (IS_ERR(mtd)) {
+               err = PTR_ERR(mtd);
+               printk(PRINT_PREF "error: cannot get MTD device\n");
+               return err;
+       }
+
+       if (mtd->writesize == 1) {
+               printk(PRINT_PREF "not NAND flash, assume page size is 512 "
+                      "bytes.\n");
+               pgsize = 512;
+       } else
+               pgsize = mtd->writesize;
+
+       tmp = mtd->size;
+       do_div(tmp, mtd->erasesize);
+       ebcnt = tmp;
+       pgcnt = mtd->erasesize / mtd->writesize;
+
+       printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
+              "page size %u, count of eraseblocks %u, pages per "
+              "eraseblock %u, OOB size %u\n",
+              (unsigned long long)mtd->size, mtd->erasesize,
+              pgsize, ebcnt, pgcnt, mtd->oobsize);
+
+       err = -ENOMEM;
+       iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!iobuf) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+
+       simple_srand(1);
+       set_random_data(iobuf, mtd->erasesize);
+
+       err = scan_for_bad_eraseblocks();
+       if (err)
+               goto out;
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       /* Write all eraseblocks, 1 eraseblock at a time */
+       printk(PRINT_PREF "testing eraseblock write speed\n");
+       start_timing();
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = write_eraseblock(i);
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       stop_timing();
+       speed = calc_speed();
+       printk(PRINT_PREF "eraseblock write speed is %ld KiB/s\n", speed);
+
+       /* Read all eraseblocks, 1 eraseblock at a time */
+       printk(PRINT_PREF "testing eraseblock read speed\n");
+       start_timing();
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = read_eraseblock(i);
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       stop_timing();
+       speed = calc_speed();
+       printk(PRINT_PREF "eraseblock read speed is %ld KiB/s\n", speed);
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       /* Write all eraseblocks, 1 page at a time */
+       printk(PRINT_PREF "testing page write speed\n");
+       start_timing();
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = write_eraseblock_by_page(i);
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       stop_timing();
+       speed = calc_speed();
+       printk(PRINT_PREF "page write speed is %ld KiB/s\n", speed);
+
+       /* Read all eraseblocks, 1 page at a time */
+       printk(PRINT_PREF "testing page read speed\n");
+       start_timing();
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = read_eraseblock_by_page(i);
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       stop_timing();
+       speed = calc_speed();
+       printk(PRINT_PREF "page read speed is %ld KiB/s\n", speed);
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       /* Write all eraseblocks, 2 pages at a time */
+       printk(PRINT_PREF "testing 2 page write speed\n");
+       start_timing();
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = write_eraseblock_by_2pages(i);
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       stop_timing();
+       speed = calc_speed();
+       printk(PRINT_PREF "2 page write speed is %ld KiB/s\n", speed);
+
+       /* Read all eraseblocks, 2 pages at a time */
+       printk(PRINT_PREF "testing 2 page read speed\n");
+       start_timing();
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = read_eraseblock_by_2pages(i);
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       stop_timing();
+       speed = calc_speed();
+       printk(PRINT_PREF "2 page read speed is %ld KiB/s\n", speed);
+
+       /* Erase all eraseblocks */
+       printk(PRINT_PREF "Testing erase speed\n");
+       start_timing();
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = erase_eraseblock(i);
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       stop_timing();
+       speed = calc_speed();
+       printk(PRINT_PREF "erase speed is %ld KiB/s\n", speed);
+
+       printk(PRINT_PREF "finished\n");
+out:
+       kfree(iobuf);
+       kfree(bbt);
+       put_mtd_device(mtd);
+       if (err)
+               printk(PRINT_PREF "error %d occurred\n", err);
+       printk(KERN_INFO "=================================================\n");
+       return err;
+}
+module_init(mtd_speedtest_init);
+
+static void __exit mtd_speedtest_exit(void)
+{
+       return;
+}
+module_exit(mtd_speedtest_exit);
+
+MODULE_DESCRIPTION("Speed test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c
new file mode 100644 (file)
index 0000000..6392047
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test random reads, writes and erases on MTD device.
+ *
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#define PRINT_PREF KERN_INFO "mtd_stresstest: "
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static int count = 10000;
+module_param(count, int, S_IRUGO);
+MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)");
+
+static struct mtd_info *mtd;
+static unsigned char *writebuf;
+static unsigned char *readbuf;
+static unsigned char *bbt;
+static int *offsets;
+
+static int pgsize;
+static int bufsize;
+static int ebcnt;
+static int pgcnt;
+static unsigned long next = 1;
+
+static inline unsigned int simple_rand(void)
+{
+       next = next * 1103515245 + 12345;
+       return (unsigned int)((next / 65536) % 32768);
+}
+
+static inline void simple_srand(unsigned long seed)
+{
+       next = seed;
+}
+
+static int rand_eb(void)
+{
+       int eb;
+
+again:
+       if (ebcnt < 32768)
+               eb = simple_rand();
+       else
+               eb = (simple_rand() << 15) | simple_rand();
+       /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */
+       eb %= (ebcnt - 1);
+       if (bbt[eb])
+               goto again;
+       return eb;
+}
+
+static int rand_offs(void)
+{
+       int offs;
+
+       if (bufsize < 32768)
+               offs = simple_rand();
+       else
+               offs = (simple_rand() << 15) | simple_rand();
+       offs %= bufsize;
+       return offs;
+}
+
+static int rand_len(int offs)
+{
+       int len;
+
+       if (bufsize < 32768)
+               len = simple_rand();
+       else
+               len = (simple_rand() << 15) | simple_rand();
+       len %= (bufsize - offs);
+       return len;
+}
+
+static int erase_eraseblock(int ebnum)
+{
+       int err;
+       struct erase_info ei;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       memset(&ei, 0, sizeof(struct erase_info));
+       ei.mtd  = mtd;
+       ei.addr = addr;
+       ei.len  = mtd->erasesize;
+
+       err = mtd->erase(mtd, &ei);
+       if (unlikely(err)) {
+               printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
+               return err;
+       }
+
+       if (unlikely(ei.state == MTD_ERASE_FAILED)) {
+               printk(PRINT_PREF "some erase error occurred at EB %d\n",
+                      ebnum);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int is_block_bad(int ebnum)
+{
+       loff_t addr = ebnum * mtd->erasesize;
+       int ret;
+
+       ret = mtd->block_isbad(mtd, addr);
+       if (ret)
+               printk(PRINT_PREF "block %d is bad\n", ebnum);
+       return ret;
+}
+
+static int do_read(void)
+{
+       size_t read = 0;
+       int eb = rand_eb();
+       int offs = rand_offs();
+       int len = rand_len(offs), err;
+       loff_t addr;
+
+       if (bbt[eb + 1]) {
+               if (offs >= mtd->erasesize)
+                       offs -= mtd->erasesize;
+               if (offs + len > mtd->erasesize)
+                       len = mtd->erasesize - offs;
+       }
+       addr = eb * mtd->erasesize + offs;
+       err = mtd->read(mtd, addr, len, &read, readbuf);
+       if (err == -EUCLEAN)
+               err = 0;
+       if (unlikely(err || read != len)) {
+               printk(PRINT_PREF "error: read failed at 0x%llx\n",
+                      (long long)addr);
+               if (!err)
+                       err = -EINVAL;
+               return err;
+       }
+       return 0;
+}
+
+static int do_write(void)
+{
+       int eb = rand_eb(), offs, err, len;
+       size_t written = 0;
+       loff_t addr;
+
+       offs = offsets[eb];
+       if (offs >= mtd->erasesize) {
+               err = erase_eraseblock(eb);
+               if (err)
+                       return err;
+               offs = offsets[eb] = 0;
+       }
+       len = rand_len(offs);
+       len = ((len + pgsize - 1) / pgsize) * pgsize;
+       if (offs + len > mtd->erasesize) {
+               if (bbt[eb + 1])
+                       len = mtd->erasesize - offs;
+               else {
+                       err = erase_eraseblock(eb + 1);
+                       if (err)
+                               return err;
+                       offsets[eb + 1] = 0;
+               }
+       }
+       addr = eb * mtd->erasesize + offs;
+       err = mtd->write(mtd, addr, len, &written, writebuf);
+       if (unlikely(err || written != len)) {
+               printk(PRINT_PREF "error: write failed at 0x%llx\n",
+                      (long long)addr);
+               if (!err)
+                       err = -EINVAL;
+               return err;
+       }
+       offs += len;
+       while (offs > mtd->erasesize) {
+               offsets[eb++] = mtd->erasesize;
+               offs -= mtd->erasesize;
+       }
+       offsets[eb] = offs;
+       return 0;
+}
+
+static int do_operation(void)
+{
+       if (simple_rand() & 1)
+               return do_read();
+       else
+               return do_write();
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+       int i, bad = 0;
+
+       bbt = kmalloc(ebcnt, GFP_KERNEL);
+       if (!bbt) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               return -ENOMEM;
+       }
+       memset(bbt, 0 , ebcnt);
+
+       printk(PRINT_PREF "scanning for bad eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               bbt[i] = is_block_bad(i) ? 1 : 0;
+               if (bbt[i])
+                       bad += 1;
+               cond_resched();
+       }
+       printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
+       return 0;
+}
+
+static int __init mtd_stresstest_init(void)
+{
+       int err;
+       int i, op;
+       uint64_t tmp;
+
+       printk(KERN_INFO "\n");
+       printk(KERN_INFO "=================================================\n");
+       printk(PRINT_PREF "MTD device: %d\n", dev);
+
+       mtd = get_mtd_device(NULL, dev);
+       if (IS_ERR(mtd)) {
+               err = PTR_ERR(mtd);
+               printk(PRINT_PREF "error: cannot get MTD device\n");
+               return err;
+       }
+
+       if (mtd->writesize == 1) {
+               printk(PRINT_PREF "not NAND flash, assume page size is 512 "
+                      "bytes.\n");
+               pgsize = 512;
+       } else
+               pgsize = mtd->writesize;
+
+       tmp = mtd->size;
+       do_div(tmp, mtd->erasesize);
+       ebcnt = tmp;
+       pgcnt = mtd->erasesize / mtd->writesize;
+
+       printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
+              "page size %u, count of eraseblocks %u, pages per "
+              "eraseblock %u, OOB size %u\n",
+              (unsigned long long)mtd->size, mtd->erasesize,
+              pgsize, ebcnt, pgcnt, mtd->oobsize);
+
+       /* Read or write up 2 eraseblocks at a time */
+       bufsize = mtd->erasesize * 2;
+
+       err = -ENOMEM;
+       readbuf = vmalloc(bufsize);
+       writebuf = vmalloc(bufsize);
+       offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL);
+       if (!readbuf || !writebuf || !offsets) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+       for (i = 0; i < ebcnt; i++)
+               offsets[i] = mtd->erasesize;
+       simple_srand(current->pid);
+       for (i = 0; i < bufsize; i++)
+               writebuf[i] = simple_rand();
+
+       err = scan_for_bad_eraseblocks();
+       if (err)
+               goto out;
+
+       /* Do operations */
+       printk(PRINT_PREF "doing operations\n");
+       for (op = 0; op < count; op++) {
+               if ((op & 1023) == 0)
+                       printk(PRINT_PREF "%d operations done\n", op);
+               err = do_operation();
+               if (err)
+                       goto out;
+               cond_resched();
+       }
+       printk(PRINT_PREF "finished, %d operations done\n", op);
+
+out:
+       kfree(offsets);
+       kfree(bbt);
+       vfree(writebuf);
+       vfree(readbuf);
+       put_mtd_device(mtd);
+       if (err)
+               printk(PRINT_PREF "error %d occurred\n", err);
+       printk(KERN_INFO "=================================================\n");
+       return err;
+}
+module_init(mtd_stresstest_init);
+
+static void __exit mtd_stresstest_exit(void)
+{
+       return;
+}
+module_exit(mtd_stresstest_exit);
+
+MODULE_DESCRIPTION("Stress test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c
new file mode 100644 (file)
index 0000000..5b88972
--- /dev/null
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2006-2007 Nokia Corporation
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Test sub-page read and write on MTD device.
+ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sched.h>
+
+#define PRINT_PREF KERN_INFO "mtd_subpagetest: "
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static struct mtd_info *mtd;
+static unsigned char *writebuf;
+static unsigned char *readbuf;
+static unsigned char *bbt;
+
+static int subpgsize;
+static int bufsize;
+static int ebcnt;
+static int pgcnt;
+static int errcnt;
+static unsigned long next = 1;
+
+static inline unsigned int simple_rand(void)
+{
+       next = next * 1103515245 + 12345;
+       return (unsigned int)((next / 65536) % 32768);
+}
+
+static inline void simple_srand(unsigned long seed)
+{
+       next = seed;
+}
+
+static void set_random_data(unsigned char *buf, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; ++i)
+               buf[i] = simple_rand();
+}
+
+static inline void clear_data(unsigned char *buf, size_t len)
+{
+       memset(buf, 0, len);
+}
+
+static int erase_eraseblock(int ebnum)
+{
+       int err;
+       struct erase_info ei;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       memset(&ei, 0, sizeof(struct erase_info));
+       ei.mtd  = mtd;
+       ei.addr = addr;
+       ei.len  = mtd->erasesize;
+
+       err = mtd->erase(mtd, &ei);
+       if (err) {
+               printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
+               return err;
+       }
+
+       if (ei.state == MTD_ERASE_FAILED) {
+               printk(PRINT_PREF "some erase error occurred at EB %d\n",
+                      ebnum);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int erase_whole_device(void)
+{
+       int err;
+       unsigned int i;
+
+       printk(PRINT_PREF "erasing whole device\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = erase_eraseblock(i);
+               if (err)
+                       return err;
+               cond_resched();
+       }
+       printk(PRINT_PREF "erased %u eraseblocks\n", i);
+       return 0;
+}
+
+static int write_eraseblock(int ebnum)
+{
+       size_t written = 0;
+       int err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       set_random_data(writebuf, subpgsize);
+       err = mtd->write(mtd, addr, subpgsize, &written, writebuf);
+       if (unlikely(err || written != subpgsize)) {
+               printk(PRINT_PREF "error: write failed at %#llx\n",
+                      (long long)addr);
+               if (written != subpgsize) {
+                       printk(PRINT_PREF "  write size: %#x\n", subpgsize);
+                       printk(PRINT_PREF "  written: %#zx\n", written);
+               }
+               return err ? err : -1;
+       }
+
+       addr += subpgsize;
+
+       set_random_data(writebuf, subpgsize);
+       err = mtd->write(mtd, addr, subpgsize, &written, writebuf);
+       if (unlikely(err || written != subpgsize)) {
+               printk(PRINT_PREF "error: write failed at %#llx\n",
+                      (long long)addr);
+               if (written != subpgsize) {
+                       printk(PRINT_PREF "  write size: %#x\n", subpgsize);
+                       printk(PRINT_PREF "  written: %#zx\n", written);
+               }
+               return err ? err : -1;
+       }
+
+       return err;
+}
+
+static int write_eraseblock2(int ebnum)
+{
+       size_t written = 0;
+       int err = 0, k;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       for (k = 1; k < 33; ++k) {
+               if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
+                       break;
+               set_random_data(writebuf, subpgsize * k);
+               err = mtd->write(mtd, addr, subpgsize * k, &written, writebuf);
+               if (unlikely(err || written != subpgsize * k)) {
+                       printk(PRINT_PREF "error: write failed at %#llx\n",
+                              (long long)addr);
+                       if (written != subpgsize) {
+                               printk(PRINT_PREF "  write size: %#x\n",
+                                      subpgsize * k);
+                               printk(PRINT_PREF "  written: %#08zx\n",
+                                      written);
+                       }
+                       return err ? err : -1;
+               }
+               addr += subpgsize * k;
+       }
+
+       return err;
+}
+
+static void print_subpage(unsigned char *p)
+{
+       int i, j;
+
+       for (i = 0; i < subpgsize; ) {
+               for (j = 0; i < subpgsize && j < 32; ++i, ++j)
+                       printk("%02x", *p++);
+               printk("\n");
+       }
+}
+
+static int verify_eraseblock(int ebnum)
+{
+       size_t read = 0;
+       int err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       set_random_data(writebuf, subpgsize);
+       clear_data(readbuf, subpgsize);
+       read = 0;
+       err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
+       if (unlikely(err || read != subpgsize)) {
+               if (err == -EUCLEAN && read == subpgsize) {
+                       printk(PRINT_PREF "ECC correction at %#llx\n",
+                              (long long)addr);
+                       err = 0;
+               } else {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)addr);
+                       return err ? err : -1;
+               }
+       }
+       if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
+               printk(PRINT_PREF "error: verify failed at %#llx\n",
+                      (long long)addr);
+               printk(PRINT_PREF "------------- written----------------\n");
+               print_subpage(writebuf);
+               printk(PRINT_PREF "------------- read ------------------\n");
+               print_subpage(readbuf);
+               printk(PRINT_PREF "-------------------------------------\n");
+               errcnt += 1;
+       }
+
+       addr += subpgsize;
+
+       set_random_data(writebuf, subpgsize);
+       clear_data(readbuf, subpgsize);
+       read = 0;
+       err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
+       if (unlikely(err || read != subpgsize)) {
+               if (err == -EUCLEAN && read == subpgsize) {
+                       printk(PRINT_PREF "ECC correction at %#llx\n",
+                              (long long)addr);
+                       err = 0;
+               } else {
+                       printk(PRINT_PREF "error: read failed at %#llx\n",
+                              (long long)addr);
+                       return err ? err : -1;
+               }
+       }
+       if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
+               printk(PRINT_PREF "error: verify failed at %#llx\n",
+                      (long long)addr);
+               printk(PRINT_PREF "------------- written----------------\n");
+               print_subpage(writebuf);
+               printk(PRINT_PREF "------------- read ------------------\n");
+               print_subpage(readbuf);
+               printk(PRINT_PREF "-------------------------------------\n");
+               errcnt += 1;
+       }
+
+       return err;
+}
+
+static int verify_eraseblock2(int ebnum)
+{
+       size_t read = 0;
+       int err = 0, k;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       for (k = 1; k < 33; ++k) {
+               if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
+                       break;
+               set_random_data(writebuf, subpgsize * k);
+               clear_data(readbuf, subpgsize * k);
+               read = 0;
+               err = mtd->read(mtd, addr, subpgsize * k, &read, readbuf);
+               if (unlikely(err || read != subpgsize * k)) {
+                       if (err == -EUCLEAN && read == subpgsize * k) {
+                               printk(PRINT_PREF "ECC correction at %#llx\n",
+                                      (long long)addr);
+                               err = 0;
+                       } else {
+                               printk(PRINT_PREF "error: read failed at "
+                                      "%#llx\n", (long long)addr);
+                               return err ? err : -1;
+                       }
+               }
+               if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) {
+                       printk(PRINT_PREF "error: verify failed at %#llx\n",
+                              (long long)addr);
+                       errcnt += 1;
+               }
+               addr += subpgsize * k;
+       }
+
+       return err;
+}
+
+static int verify_eraseblock_ff(int ebnum)
+{
+       uint32_t j;
+       size_t read = 0;
+       int err = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       memset(writebuf, 0xff, subpgsize);
+       for (j = 0; j < mtd->erasesize / subpgsize; ++j) {
+               clear_data(readbuf, subpgsize);
+               read = 0;
+               err = mtd->read(mtd, addr, subpgsize, &read, readbuf);
+               if (unlikely(err || read != subpgsize)) {
+                       if (err == -EUCLEAN && read == subpgsize) {
+                               printk(PRINT_PREF "ECC correction at %#llx\n",
+                                      (long long)addr);
+                               err = 0;
+                       } else {
+                               printk(PRINT_PREF "error: read failed at "
+                                      "%#llx\n", (long long)addr);
+                               return err ? err : -1;
+                       }
+               }
+               if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
+                       printk(PRINT_PREF "error: verify 0xff failed at "
+                              "%#llx\n", (long long)addr);
+                       errcnt += 1;
+               }
+               addr += subpgsize;
+       }
+
+       return err;
+}
+
+static int verify_all_eraseblocks_ff(void)
+{
+       int err;
+       unsigned int i;
+
+       printk(PRINT_PREF "verifying all eraseblocks for 0xff\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = verify_eraseblock_ff(i);
+               if (err)
+                       return err;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "verified up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "verified %u eraseblocks\n", i);
+       return 0;
+}
+
+static int is_block_bad(int ebnum)
+{
+       loff_t addr = ebnum * mtd->erasesize;
+       int ret;
+
+       ret = mtd->block_isbad(mtd, addr);
+       if (ret)
+               printk(PRINT_PREF "block %d is bad\n", ebnum);
+       return ret;
+}
+
+static int scan_for_bad_eraseblocks(void)
+{
+       int i, bad = 0;
+
+       bbt = kmalloc(ebcnt, GFP_KERNEL);
+       if (!bbt) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               return -ENOMEM;
+       }
+       memset(bbt, 0 , ebcnt);
+
+       printk(PRINT_PREF "scanning for bad eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               bbt[i] = is_block_bad(i) ? 1 : 0;
+               if (bbt[i])
+                       bad += 1;
+               cond_resched();
+       }
+       printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
+       return 0;
+}
+
+static int __init mtd_subpagetest_init(void)
+{
+       int err = 0;
+       uint32_t i;
+       uint64_t tmp;
+
+       printk(KERN_INFO "\n");
+       printk(KERN_INFO "=================================================\n");
+       printk(PRINT_PREF "MTD device: %d\n", dev);
+
+       mtd = get_mtd_device(NULL, dev);
+       if (IS_ERR(mtd)) {
+               err = PTR_ERR(mtd);
+               printk(PRINT_PREF "error: cannot get MTD device\n");
+               return err;
+       }
+
+       if (mtd->type != MTD_NANDFLASH) {
+               printk(PRINT_PREF "this test requires NAND flash\n");
+               goto out;
+       }
+
+       subpgsize = mtd->writesize >> mtd->subpage_sft;
+       printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
+              "page size %u, subpage size %u, count of eraseblocks %u, "
+              "pages per eraseblock %u, OOB size %u\n",
+              (unsigned long long)mtd->size, mtd->erasesize,
+              mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize);
+
+       err = -ENOMEM;
+       bufsize = subpgsize * 32;
+       writebuf = kmalloc(bufsize, GFP_KERNEL);
+       if (!writebuf) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+       readbuf = kmalloc(bufsize, GFP_KERNEL);
+       if (!readbuf) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out;
+       }
+
+       tmp = mtd->size;
+       do_div(tmp, mtd->erasesize);
+       ebcnt = tmp;
+       pgcnt = mtd->erasesize / mtd->writesize;
+
+       err = scan_for_bad_eraseblocks();
+       if (err)
+               goto out;
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       printk(PRINT_PREF "writing whole device\n");
+       simple_srand(1);
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = write_eraseblock(i);
+               if (unlikely(err))
+                       goto out;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "written up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "written %u eraseblocks\n", i);
+
+       simple_srand(1);
+       printk(PRINT_PREF "verifying all eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = verify_eraseblock(i);
+               if (unlikely(err))
+                       goto out;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "verified up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "verified %u eraseblocks\n", i);
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       err = verify_all_eraseblocks_ff();
+       if (err)
+               goto out;
+
+       /* Write all eraseblocks */
+       simple_srand(3);
+       printk(PRINT_PREF "writing whole device\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = write_eraseblock2(i);
+               if (unlikely(err))
+                       goto out;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "written up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "written %u eraseblocks\n", i);
+
+       /* Check all eraseblocks */
+       simple_srand(3);
+       printk(PRINT_PREF "verifying all eraseblocks\n");
+       for (i = 0; i < ebcnt; ++i) {
+               if (bbt[i])
+                       continue;
+               err = verify_eraseblock2(i);
+               if (unlikely(err))
+                       goto out;
+               if (i % 256 == 0)
+                       printk(PRINT_PREF "verified up to eraseblock %u\n", i);
+               cond_resched();
+       }
+       printk(PRINT_PREF "verified %u eraseblocks\n", i);
+
+       err = erase_whole_device();
+       if (err)
+               goto out;
+
+       err = verify_all_eraseblocks_ff();
+       if (err)
+               goto out;
+
+       printk(PRINT_PREF "finished with %d errors\n", errcnt);
+
+out:
+       kfree(bbt);
+       kfree(readbuf);
+       kfree(writebuf);
+       put_mtd_device(mtd);
+       if (err)
+               printk(PRINT_PREF "error %d occurred\n", err);
+       printk(KERN_INFO "=================================================\n");
+       return err;
+}
+module_init(mtd_subpagetest_init);
+
+static void __exit mtd_subpagetest_exit(void)
+{
+       return;
+}
+module_exit(mtd_subpagetest_exit);
+
+MODULE_DESCRIPTION("Subpage test module");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c
new file mode 100644 (file)
index 0000000..631a0ab
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2006-2008 Artem Bityutskiy
+ * Copyright (C) 2006-2008 Jarkko Lavinen
+ * Copyright (C) 2006-2008 Adrian Hunter
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter
+ *
+ * WARNING: this test program may kill your flash and your device. Do not
+ * use it unless you know what you do. Authors are not responsible for any
+ * damage caused by this program.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sched.h>
+
+#define PRINT_PREF KERN_INFO "mtd_torturetest: "
+#define RETRIES 3
+
+static int eb = 8;
+module_param(eb, int, S_IRUGO);
+MODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device");
+
+static int ebcnt = 32;
+module_param(ebcnt, int, S_IRUGO);
+MODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture");
+
+static int pgcnt;
+module_param(pgcnt, int, S_IRUGO);
+MODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)");
+
+static int dev;
+module_param(dev, int, S_IRUGO);
+MODULE_PARM_DESC(dev, "MTD device number to use");
+
+static int gran = 512;
+module_param(gran, int, S_IRUGO);
+MODULE_PARM_DESC(gran, "how often the status information should be printed");
+
+static int check = 1;
+module_param(check, int, S_IRUGO);
+MODULE_PARM_DESC(check, "if the written data should be checked");
+
+static unsigned int cycles_count;
+module_param(cycles_count, uint, S_IRUGO);
+MODULE_PARM_DESC(cycles_count, "how many erase cycles to do "
+                              "(infinite by default)");
+
+static struct mtd_info *mtd;
+
+/* This buffer contains 0x555555...0xAAAAAA... pattern */
+static unsigned char *patt_5A5;
+/* This buffer contains 0xAAAAAA...0x555555... pattern */
+static unsigned char *patt_A5A;
+/* This buffer contains all 0xFF bytes */
+static unsigned char *patt_FF;
+/* This a temporary buffer is use when checking data */
+static unsigned char *check_buf;
+/* How many erase cycles were done */
+static unsigned int erase_cycles;
+
+static int pgsize;
+static struct timeval start, finish;
+
+static void report_corrupt(unsigned char *read, unsigned char *written);
+
+static inline void start_timing(void)
+{
+       do_gettimeofday(&start);
+}
+
+static inline void stop_timing(void)
+{
+       do_gettimeofday(&finish);
+}
+
+/*
+ * Erase eraseblock number @ebnum.
+ */
+static inline int erase_eraseblock(int ebnum)
+{
+       int err;
+       struct erase_info ei;
+       loff_t addr = ebnum * mtd->erasesize;
+
+       memset(&ei, 0, sizeof(struct erase_info));
+       ei.mtd  = mtd;
+       ei.addr = addr;
+       ei.len  = mtd->erasesize;
+
+       err = mtd->erase(mtd, &ei);
+       if (err) {
+               printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum);
+               return err;
+       }
+
+       if (ei.state == MTD_ERASE_FAILED) {
+               printk(PRINT_PREF "some erase error occurred at EB %d\n",
+                      ebnum);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/*
+ * Check that the contents of eraseblock number @enbum is equivalent to the
+ * @buf buffer.
+ */
+static inline int check_eraseblock(int ebnum, unsigned char *buf)
+{
+       int err, retries = 0;
+       size_t read = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+       size_t len = mtd->erasesize;
+
+       if (pgcnt) {
+               addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
+               len = pgcnt * pgsize;
+       }
+
+retry:
+       err = mtd->read(mtd, addr, len, &read, check_buf);
+       if (err == -EUCLEAN)
+               printk(PRINT_PREF "single bit flip occurred at EB %d "
+                      "MTD reported that it was fixed.\n", ebnum);
+       else if (err) {
+               printk(PRINT_PREF "error %d while reading EB %d, "
+                      "read %zd\n", err, ebnum, read);
+               return err;
+       }
+
+       if (read != len) {
+               printk(PRINT_PREF "failed to read %zd bytes from EB %d, "
+                      "read only %zd, but no error reported\n",
+                      len, ebnum, read);
+               return -EIO;
+       }
+
+       if (memcmp(buf, check_buf, len)) {
+               printk(PRINT_PREF "read wrong data from EB %d\n", ebnum);
+               report_corrupt(check_buf, buf);
+
+               if (retries++ < RETRIES) {
+                       /* Try read again */
+                       yield();
+                       printk(PRINT_PREF "re-try reading data from EB %d\n",
+                              ebnum);
+                       goto retry;
+               } else {
+                       printk(PRINT_PREF "retried %d times, still errors, "
+                              "give-up\n", RETRIES);
+                       return -EINVAL;
+               }
+       }
+
+       if (retries != 0)
+               printk(PRINT_PREF "only attempt number %d was OK (!!!)\n",
+                      retries);
+
+       return 0;
+}
+
+static inline int write_pattern(int ebnum, void *buf)
+{
+       int err;
+       size_t written = 0;
+       loff_t addr = ebnum * mtd->erasesize;
+       size_t len = mtd->erasesize;
+
+       if (pgcnt) {
+               addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
+               len = pgcnt * pgsize;
+       }
+       err = mtd->write(mtd, addr, len, &written, buf);
+       if (err) {
+               printk(PRINT_PREF "error %d while writing EB %d, written %zd"
+                     " bytes\n", err, ebnum, written);
+               return err;
+       }
+       if (written != len) {
+               printk(PRINT_PREF "written only %zd bytes of %zd, but no error"
+                      " reported\n", written, len);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int __init tort_init(void)
+{
+       int err = 0, i, infinite = !cycles_count;
+       int bad_ebs[ebcnt];
+
+       printk(KERN_INFO "\n");
+       printk(KERN_INFO "=================================================\n");
+       printk(PRINT_PREF "Warning: this program is trying to wear out your "
+              "flash, stop it if this is not wanted.\n");
+       printk(PRINT_PREF "MTD device: %d\n", dev);
+       printk(PRINT_PREF "torture %d eraseblocks (%d-%d) of mtd%d\n",
+              ebcnt, eb, eb + ebcnt - 1, dev);
+       if (pgcnt)
+               printk(PRINT_PREF "torturing just %d pages per eraseblock\n",
+                       pgcnt);
+       printk(PRINT_PREF "write verify %s\n", check ? "enabled" : "disabled");
+
+       mtd = get_mtd_device(NULL, dev);
+       if (IS_ERR(mtd)) {
+               err = PTR_ERR(mtd);
+               printk(PRINT_PREF "error: cannot get MTD device\n");
+               return err;
+       }
+
+       if (mtd->writesize == 1) {
+               printk(PRINT_PREF "not NAND flash, assume page size is 512 "
+                      "bytes.\n");
+               pgsize = 512;
+       } else
+               pgsize = mtd->writesize;
+
+       if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) {
+               printk(PRINT_PREF "error: invalid pgcnt value %d\n", pgcnt);
+               goto out_mtd;
+       }
+
+       err = -ENOMEM;
+       patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!patt_5A5) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out_mtd;
+       }
+
+       patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!patt_A5A) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out_patt_5A5;
+       }
+
+       patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!patt_FF) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out_patt_A5A;
+       }
+
+       check_buf = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!check_buf) {
+               printk(PRINT_PREF "error: cannot allocate memory\n");
+               goto out_patt_FF;
+       }
+
+       err = 0;
+
+       /* Initialize patterns */
+       memset(patt_FF, 0xFF, mtd->erasesize);
+       for (i = 0; i < mtd->erasesize / pgsize; i++) {
+               if (!(i & 1)) {
+                       memset(patt_5A5 + i * pgsize, 0x55, pgsize);
+                       memset(patt_A5A + i * pgsize, 0xAA, pgsize);
+               } else {
+                       memset(patt_5A5 + i * pgsize, 0xAA, pgsize);
+                       memset(patt_A5A + i * pgsize, 0x55, pgsize);
+               }
+       }
+
+       /*
+        * Check if there is a bad eraseblock among those we are going to test.
+        */
+       memset(&bad_ebs[0], 0, sizeof(int) * ebcnt);
+       if (mtd->block_isbad) {
+               for (i = eb; i < eb + ebcnt; i++) {
+                       err = mtd->block_isbad(mtd,
+                                              (loff_t)i * mtd->erasesize);
+
+                       if (err < 0) {
+                               printk(PRINT_PREF "block_isbad() returned %d "
+                                      "for EB %d\n", err, i);
+                               goto out;
+                       }
+
+                       if (err) {
+                               printk("EB %d is bad. Skip it.\n", i);
+                               bad_ebs[i - eb] = 1;
+                       }
+               }
+       }
+
+       start_timing();
+       while (1) {
+               int i;
+               void *patt;
+
+               /* Erase all eraseblocks */
+               for (i = eb; i < eb + ebcnt; i++) {
+                       if (bad_ebs[i - eb])
+                               continue;
+                       err = erase_eraseblock(i);
+                       if (err)
+                               goto out;
+                       cond_resched();
+               }
+
+               /* Check if the eraseblocks contain only 0xFF bytes */
+               if (check) {
+                       for (i = eb; i < eb + ebcnt; i++) {
+                               if (bad_ebs[i - eb])
+                                       continue;
+                               err = check_eraseblock(i, patt_FF);
+                               if (err) {
+                                       printk(PRINT_PREF "verify failed"
+                                              " for 0xFF... pattern\n");
+                                       goto out;
+                               }
+                               cond_resched();
+                       }
+               }
+
+               /* Write the pattern */
+               for (i = eb; i < eb + ebcnt; i++) {
+                       if (bad_ebs[i - eb])
+                               continue;
+                       if ((eb + erase_cycles) & 1)
+                               patt = patt_5A5;
+                       else
+                               patt = patt_A5A;
+                       err = write_pattern(i, patt);
+                       if (err)
+                               goto out;
+                       cond_resched();
+               }
+
+               /* Verify what we wrote */
+               if (check) {
+                       for (i = eb; i < eb + ebcnt; i++) {
+                               if (bad_ebs[i - eb])
+                                       continue;
+                               if ((eb + erase_cycles) & 1)
+                                       patt = patt_5A5;
+                               else
+                                       patt = patt_A5A;
+                               err = check_eraseblock(i, patt);
+                               if (err) {
+                                       printk(PRINT_PREF "verify failed for %s"
+                                              " pattern\n",
+                                              ((eb + erase_cycles) & 1) ?
+                                              "0x55AA55..." : "0xAA55AA...");
+                                       goto out;
+                               }
+                               cond_resched();
+                       }
+               }
+
+               erase_cycles += 1;
+
+               if (erase_cycles % gran == 0) {
+                       long ms;
+
+                       stop_timing();
+                       ms = (finish.tv_sec - start.tv_sec) * 1000 +
+                            (finish.tv_usec - start.tv_usec) / 1000;
+                       printk(PRINT_PREF "%08u erase cycles done, took %lu "
+                              "milliseconds (%lu seconds)\n",
+                              erase_cycles, ms, ms / 1000);
+                       start_timing();
+               }
+
+               if (!infinite && --cycles_count == 0)
+                       break;
+       }
+out:
+
+       printk(PRINT_PREF "finished after %u erase cycles\n",
+              erase_cycles);
+       kfree(check_buf);
+out_patt_FF:
+       kfree(patt_FF);
+out_patt_A5A:
+       kfree(patt_A5A);
+out_patt_5A5:
+       kfree(patt_5A5);
+out_mtd:
+       put_mtd_device(mtd);
+       if (err)
+               printk(PRINT_PREF "error %d occurred during torturing\n", err);
+       printk(KERN_INFO "=================================================\n");
+       return err;
+}
+module_init(tort_init);
+
+static void __exit tort_exit(void)
+{
+       return;
+}
+module_exit(tort_exit);
+
+static int countdiffs(unsigned char *buf, unsigned char *check_buf,
+                     unsigned offset, unsigned len, unsigned *bytesp,
+                     unsigned *bitsp);
+static void print_bufs(unsigned char *read, unsigned char *written, int start,
+                      int len);
+
+/*
+ * Report the detailed information about how the read EB differs from what was
+ * written.
+ */
+static void report_corrupt(unsigned char *read, unsigned char *written)
+{
+       int i;
+       int bytes, bits, pages, first;
+       int offset, len;
+       size_t check_len = mtd->erasesize;
+
+       if (pgcnt)
+               check_len = pgcnt * pgsize;
+
+       bytes = bits = pages = 0;
+       for (i = 0; i < check_len; i += pgsize)
+               if (countdiffs(written, read, i, pgsize, &bytes,
+                              &bits) >= 0)
+                       pages++;
+
+       printk(PRINT_PREF "verify fails on %d pages, %d bytes/%d bits\n",
+              pages, bytes, bits);
+       printk(PRINT_PREF "The following is a list of all differences between"
+              " what was read from flash and what was expected\n");
+
+       for (i = 0; i < check_len; i += pgsize) {
+               cond_resched();
+               bytes = bits = 0;
+               first = countdiffs(written, read, i, pgsize, &bytes,
+                                  &bits);
+               if (first < 0)
+                       continue;
+
+               printk("-------------------------------------------------------"
+                      "----------------------------------\n");
+
+               printk(PRINT_PREF "Page %zd has %d bytes/%d bits failing verify,"
+                      " starting at offset 0x%x\n",
+                      (mtd->erasesize - check_len + i) / pgsize,
+                      bytes, bits, first);
+
+               offset = first & ~0x7;
+               len = ((first + bytes) | 0x7) + 1 - offset;
+
+               print_bufs(read, written, offset, len);
+       }
+}
+
+static void print_bufs(unsigned char *read, unsigned char *written, int start,
+                      int len)
+{
+       int i = 0, j1, j2;
+       char *diff;
+
+       printk("Offset       Read                          Written\n");
+       while (i < len) {
+               printk("0x%08x: ", start + i);
+               diff = "   ";
+               for (j1 = 0; j1 < 8 && i + j1 < len; j1++) {
+                       printk(" %02x", read[start + i + j1]);
+                       if (read[start + i + j1] != written[start + i + j1])
+                               diff = "***";
+               }
+
+               while (j1 < 8) {
+                       printk(" ");
+                       j1 += 1;
+               }
+
+               printk("  %s ", diff);
+
+               for (j2 = 0; j2 < 8 && i + j2 < len; j2++)
+                       printk(" %02x", written[start + i + j2]);
+               printk("\n");
+               i += 8;
+       }
+}
+
+/*
+ * Count the number of differing bytes and bits and return the first differing
+ * offset.
+ */
+static int countdiffs(unsigned char *buf, unsigned char *check_buf,
+                     unsigned offset, unsigned len, unsigned *bytesp,
+                     unsigned *bitsp)
+{
+       unsigned i, bit;
+       int first = -1;
+
+       for (i = offset; i < offset + len; i++)
+               if (buf[i] != check_buf[i]) {
+                       first = i;
+                       break;
+               }
+
+       while (i < offset + len) {
+               if (buf[i] != check_buf[i]) {
+                       (*bytesp)++;
+                       bit = 1;
+                       while (bit < 256) {
+                               if ((buf[i] & bit) != (check_buf[i] & bit))
+                                       (*bitsp)++;
+                               bit <<= 1;
+                       }
+               }
+               i++;
+       }
+
+       return first;
+}
+
+MODULE_DESCRIPTION("Eraseblock torturing module");
+MODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter");
+MODULE_LICENSE("GPL");
index 7caf22cd5ad0e724919dd4157b4c218505a622b7..9082768cc6c3850df4c9290e5f7487e795bc4e88 100644 (file)
@@ -561,7 +561,7 @@ static int io_init(struct ubi_device *ubi)
         */
 
        ubi->peb_size   = ubi->mtd->erasesize;
-       ubi->peb_count  = ubi->mtd->size / ubi->mtd->erasesize;
+       ubi->peb_count  = mtd_div_by_eb(ubi->mtd->size, ubi->mtd);
        ubi->flash_size = ubi->mtd->size;
 
        if (ubi->mtd->block_isbad && ubi->mtd->block_markbad)
index 605812bb0b1a3d1f5dcefa40929842c00bdbf2f1..6dd4f5e77f82bf2408de09fdc52b33888602bc20 100644 (file)
@@ -215,7 +215,8 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
        struct ubi_volume *vol;
        struct ubi_device *ubi;
 
-       dbg_gen("erase %u bytes at offset %u", instr->len, instr->addr);
+       dbg_gen("erase %llu bytes at offset %llu", (unsigned long long)instr->len,
+                (unsigned long long)instr->addr);
 
        if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize)
                return -EINVAL;
@@ -223,11 +224,11 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
        if (instr->len < 0 || instr->addr + instr->len > mtd->size)
                return -EINVAL;
 
-       if (instr->addr % mtd->writesize || instr->len % mtd->writesize)
+       if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd))
                return -EINVAL;
 
-       lnum = instr->addr / mtd->erasesize;
-       count = instr->len / mtd->erasesize;
+       lnum = mtd_div_by_eb(instr->addr, mtd);
+       count = mtd_div_by_eb(instr->len, mtd);
 
        vol = container_of(mtd, struct ubi_volume, gluebi_mtd);
        ubi = vol->ubi;
@@ -255,7 +256,7 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
 
 out_err:
        instr->state = MTD_ERASE_FAILED;
-       instr->fail_addr = lnum * mtd->erasesize;
+       instr->fail_addr = (long long)lnum * mtd->erasesize;
        return err;
 }
 
@@ -294,7 +295,7 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol)
         * bytes.
         */
        if (vol->vol_type == UBI_DYNAMIC_VOLUME)
-               mtd->size = vol->usable_leb_size * vol->reserved_pebs;
+               mtd->size = (long long)vol->usable_leb_size * vol->reserved_pebs;
        else
                mtd->size = vol->used_bytes;
 
@@ -304,8 +305,8 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol)
                return -ENFILE;
        }
 
-       dbg_gen("added mtd%d (\"%s\"), size %u, EB size %u",
-               mtd->index, mtd->name, mtd->size, mtd->erasesize);
+       dbg_gen("added mtd%d (\"%s\"), size %llu, EB size %u",
+               mtd->index, mtd->name, (unsigned long long)mtd->size, mtd->erasesize);
        return 0;
 }
 
index c73fa89b5f8a66507e59a30a0d3539673515dc8c..170d289ac785fcb09ef88004ec2a6f959465a28a 100644 (file)
@@ -22,9 +22,7 @@
 
 
 #define BIT_DIVIDER_MIPS 1043
-static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */
-
-#include <linux/errno.h>
+static int bits_mips[8] = { 277, 249, 290, 267, 229, 341, 212, 241};
 
 struct pushpull {
        unsigned char *buf;
@@ -43,7 +41,9 @@ struct rubin_state {
        int bits[8];
 };
 
-static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
+static inline void init_pushpull(struct pushpull *pp, char *buf,
+                                unsigned buflen, unsigned ofs,
+                                unsigned reserve)
 {
        pp->buf = buf;
        pp->buflen = buflen;
@@ -53,16 +53,14 @@ static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen
 
 static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
 {
-       if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
+       if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve))
                return -ENOSPC;
-       }
 
-       if (bit) {
-               pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
-       }
-       else {
-               pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
-       }
+       if (bit)
+               pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs & 7)));
+       else
+               pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs & 7)));
+
        pp->ofs++;
 
        return 0;
@@ -97,6 +95,7 @@ static void init_rubin(struct rubin_state *rs, int div, int *bits)
        rs->p = (long) (2 * UPPER_BIT_RUBIN);
        rs->bit_number = (long) 0;
        rs->bit_divider = div;
+
        for (c=0; c<8; c++)
                rs->bits[c] = bits[c];
 }
@@ -108,7 +107,8 @@ static int encode(struct rubin_state *rs, long A, long B, int symbol)
        long i0, i1;
        int ret;
 
-       while ((rs->q >= UPPER_BIT_RUBIN) || ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) {
+       while ((rs->q >= UPPER_BIT_RUBIN) ||
+              ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) {
                rs->bit_number++;
 
                ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0);
@@ -119,12 +119,12 @@ static int encode(struct rubin_state *rs, long A, long B, int symbol)
                rs->p <<= 1;
        }
        i0 = A * rs->p / (A + B);
-       if (i0 <= 0) {
+       if (i0 <= 0)
                i0 = 1;
-       }
-       if (i0 >= rs->p) {
+
+       if (i0 >= rs->p)
                i0 = rs->p - 1;
-       }
+
        i1 = rs->p - i0;
 
        if (symbol == 0)
@@ -157,11 +157,13 @@ static void init_decode(struct rubin_state *rs, int div, int *bits)
        /* behalve lower */
        rs->rec_q = 0;
 
-       for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE; rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp)))
+       for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE;
+            rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp)))
                ;
 }
 
-static void __do_decode(struct rubin_state *rs, unsigned long p, unsigned long q)
+static void __do_decode(struct rubin_state *rs, unsigned long p,
+                       unsigned long q)
 {
        register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN;
        unsigned long rec_q;
@@ -207,12 +209,11 @@ static int decode(struct rubin_state *rs, long A, long B)
                __do_decode(rs, p, q);
 
        i0 = A * rs->p / (A + B);
-       if (i0 <= 0) {
+       if (i0 <= 0)
                i0 = 1;
-       }
-       if (i0 >= rs->p) {
+
+       if (i0 >= rs->p)
                i0 = rs->p - 1;
-       }
 
        threshold = rs->q + i0;
        symbol = rs->rec_q >= threshold;
@@ -234,14 +235,15 @@ static int out_byte(struct rubin_state *rs, unsigned char byte)
        struct rubin_state rs_copy;
        rs_copy = *rs;
 
-       for (i=0;i<8;i++) {
-               ret = encode(rs, rs->bit_divider-rs->bits[i],rs->bits[i],byte&1);
+       for (i=0; i<8; i++) {
+               ret = encode(rs, rs->bit_divider-rs->bits[i],
+                            rs->bits[i], byte & 1);
                if (ret) {
                        /* Failed. Restore old state */
                        *rs = rs_copy;
                        return ret;
                }
-               byte=byte>>1;
+               byte >>= 1 ;
        }
        return 0;
 }
@@ -251,7 +253,8 @@ static int in_byte(struct rubin_state *rs)
        int i, result = 0, bit_divider = rs->bit_divider;
 
        for (i = 0; i < 8; i++)
-               result |= decode(rs, bit_divider - rs->bits[i], rs->bits[i]) << i;
+               result |= decode(rs, bit_divider - rs->bits[i],
+                                rs->bits[i]) << i;
 
        return result;
 }
@@ -259,7 +262,8 @@ static int in_byte(struct rubin_state *rs)
 
 
 static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
-                     unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen)
+                            unsigned char *cpage_out, uint32_t *sourcelen,
+                            uint32_t *dstlen)
        {
        int outpos = 0;
        int pos=0;
@@ -295,7 +299,8 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
 int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
                   uint32_t *sourcelen, uint32_t *dstlen, void *model)
 {
-       return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+       return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in,
+                                cpage_out, sourcelen, dstlen);
 }
 #endif
 static int jffs2_dynrubin_compress(unsigned char *data_in,
@@ -316,9 +321,8 @@ static int jffs2_dynrubin_compress(unsigned char *data_in,
                return -1;
 
        memset(histo, 0, 256);
-       for (i=0; i<mysrclen; i++) {
+       for (i=0; i<mysrclen; i++)
                histo[data_in[i]]++;
-       }
        memset(bits, 0, sizeof(int)*8);
        for (i=0; i<256; i++) {
                if (i&128)
@@ -346,7 +350,8 @@ static int jffs2_dynrubin_compress(unsigned char *data_in,
                cpage_out[i] = bits[i];
        }
 
-       ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen, &mydstlen);
+       ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen,
+                               &mydstlen);
        if (ret)
                return ret;
 
@@ -363,8 +368,10 @@ static int jffs2_dynrubin_compress(unsigned char *data_in,
        return 0;
 }
 
-static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
-                        unsigned char *page_out, uint32_t srclen, uint32_t destlen)
+static void rubin_do_decompress(int bit_divider, int *bits,
+                               unsigned char *cdata_in, 
+                               unsigned char *page_out, uint32_t srclen,
+                               uint32_t destlen)
 {
        int outpos = 0;
        struct rubin_state rs;
@@ -372,9 +379,8 @@ static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata
        init_pushpull(&rs.pp, cdata_in, srclen, 0, 0);
        init_decode(&rs, bit_divider, bits);
 
-       while (outpos < destlen) {
+       while (outpos < destlen)
                page_out[outpos++] = in_byte(&rs);
-       }
 }
 
 
@@ -383,7 +389,8 @@ static int jffs2_rubinmips_decompress(unsigned char *data_in,
                                      uint32_t sourcelen, uint32_t dstlen,
                                      void *model)
 {
-       rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+       rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in,
+                           cpage_out, sourcelen, dstlen);
        return 0;
 }
 
@@ -398,52 +405,53 @@ static int jffs2_dynrubin_decompress(unsigned char *data_in,
        for (c=0; c<8; c++)
                bits[c] = data_in[c];
 
-       rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
+       rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8,
+                           dstlen);
        return 0;
 }
 
 static struct jffs2_compressor jffs2_rubinmips_comp = {
-    .priority = JFFS2_RUBINMIPS_PRIORITY,
-    .name = "rubinmips",
-    .compr = JFFS2_COMPR_DYNRUBIN,
-    .compress = NULL, /*&jffs2_rubinmips_compress,*/
-    .decompress = &jffs2_rubinmips_decompress,
+       .priority = JFFS2_RUBINMIPS_PRIORITY,
+       .name = "rubinmips",
+       .compr = JFFS2_COMPR_DYNRUBIN,
+       .compress = NULL, /*&jffs2_rubinmips_compress,*/
+       .decompress = &jffs2_rubinmips_decompress,
 #ifdef JFFS2_RUBINMIPS_DISABLED
-    .disabled = 1,
+       .disabled = 1,
 #else
-    .disabled = 0,
+       .disabled = 0,
 #endif
 };
 
 int jffs2_rubinmips_init(void)
 {
-    return jffs2_register_compressor(&jffs2_rubinmips_comp);
+       return jffs2_register_compressor(&jffs2_rubinmips_comp);
 }
 
 void jffs2_rubinmips_exit(void)
 {
-    jffs2_unregister_compressor(&jffs2_rubinmips_comp);
+       jffs2_unregister_compressor(&jffs2_rubinmips_comp);
 }
 
 static struct jffs2_compressor jffs2_dynrubin_comp = {
-    .priority = JFFS2_DYNRUBIN_PRIORITY,
-    .name = "dynrubin",
-    .compr = JFFS2_COMPR_RUBINMIPS,
-    .compress = jffs2_dynrubin_compress,
-    .decompress = &jffs2_dynrubin_decompress,
+       .priority = JFFS2_DYNRUBIN_PRIORITY,
+       .name = "dynrubin",
+       .compr = JFFS2_COMPR_RUBINMIPS,
+       .compress = jffs2_dynrubin_compress,
+       .decompress = &jffs2_dynrubin_decompress,
 #ifdef JFFS2_DYNRUBIN_DISABLED
-    .disabled = 1,
+       .disabled = 1,
 #else
-    .disabled = 0,
+       .disabled = 0,
 #endif
 };
 
 int jffs2_dynrubin_init(void)
 {
-    return jffs2_register_compressor(&jffs2_dynrubin_comp);
+       return jffs2_register_compressor(&jffs2_dynrubin_comp);
 }
 
 void jffs2_dynrubin_exit(void)
 {
-    jffs2_unregister_compressor(&jffs2_dynrubin_comp);
+       jffs2_unregister_compressor(&jffs2_dynrubin_comp);
 }
index 259461b910afec4542ea5f13af00bf0c63b3bf42..c32b4a1ad6cf6ddbdb5243fab9191986195c9395 100644 (file)
@@ -175,7 +175,7 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock
 {
        /* For NAND, if the failure did not occur at the device level for a
           specific physical page, don't bother updating the bad block table. */
-       if (jffs2_cleanmarker_oob(c) && (bad_offset != MTD_FAIL_ADDR_UNKNOWN)) {
+       if (jffs2_cleanmarker_oob(c) && (bad_offset != (uint32_t)MTD_FAIL_ADDR_UNKNOWN)) {
                /* We had a device-level failure to erase.  Let's see if we've
                   failed too many times. */
                if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
@@ -209,7 +209,8 @@ static void jffs2_erase_callback(struct erase_info *instr)
        struct erase_priv_struct *priv = (void *)instr->priv;
 
        if(instr->state != MTD_ERASE_DONE) {
-               printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
+               printk(KERN_WARNING "Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n",
+                       (unsigned long long)instr->addr, instr->state);
                jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
        } else {
                jffs2_erase_succeeded(priv->c, priv->jeb);
index 00e2b575021f6d8f6ee1fd819cf18137baf3fcd5..88d3d8fbf9f2d502855af56d8d800a52f7b1e129 100644 (file)
@@ -520,6 +520,7 @@ struct cfi_fixup {
 
 #define CFI_MFR_AMD 0x0001
 #define CFI_MFR_ATMEL 0x001F
+#define CFI_MFR_SAMSUNG 0x00EC
 #define CFI_MFR_ST  0x0020     /* STMicroelectronics */
 
 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
index 0be442f881dd66eb9e324f4f9da0790afa1ab4d4..0555f7a0b9edcfe59d3ec6394a2ffa9b8b7c8ca7 100644 (file)
 #define _LINUX_FTL_H
 
 typedef struct erase_unit_header_t {
-    u_int8_t   LinkTargetTuple[5];
-    u_int8_t   DataOrgTuple[10];
-    u_int8_t   NumTransferUnits;
-    u_int32_t  EraseCount;
-    u_int16_t  LogicalEUN;
-    u_int8_t   BlockSize;
-    u_int8_t   EraseUnitSize;
-    u_int16_t  FirstPhysicalEUN;
-    u_int16_t  NumEraseUnits;
-    u_int32_t  FormattedSize;
-    u_int32_t  FirstVMAddress;
-    u_int16_t  NumVMPages;
-    u_int8_t   Flags;
-    u_int8_t   Code;
-    u_int32_t  SerialNumber;
-    u_int32_t  AltEUHOffset;
-    u_int32_t  BAMOffset;
-    u_int8_t   Reserved[12];
-    u_int8_t   EndTuple[2];
+    uint8_t    LinkTargetTuple[5];
+    uint8_t    DataOrgTuple[10];
+    uint8_t    NumTransferUnits;
+    uint32_t   EraseCount;
+    uint16_t   LogicalEUN;
+    uint8_t    BlockSize;
+    uint8_t    EraseUnitSize;
+    uint16_t   FirstPhysicalEUN;
+    uint16_t   NumEraseUnits;
+    uint32_t   FormattedSize;
+    uint32_t   FirstVMAddress;
+    uint16_t   NumVMPages;
+    uint8_t    Flags;
+    uint8_t    Code;
+    uint32_t   SerialNumber;
+    uint32_t   AltEUHOffset;
+    uint32_t   BAMOffset;
+    uint8_t    Reserved[12];
+    uint8_t    EndTuple[2];
 } erase_unit_header_t;
 
 /* Flags in erase_unit_header_t */
index aa30244492c6770a5ccdaf9c6a21536c9f6ea579..b981b8772217cfb27e1c02e814727817e367c101 100644 (file)
@@ -223,6 +223,7 @@ struct map_info {
           must leave it enabled. */
        void (*set_vpp)(struct map_info *, int);
 
+       unsigned long pfow_base;
        unsigned long map_priv_1;
        unsigned long map_priv_2;
        void *fldrv_priv;
index 64433eb411d797c1988efff38b44471ee0587987..3aa5d77c2cdb0053d3c15c5c5bc48b05b97d93a6 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/mtd/compatmac.h>
 #include <mtd/mtd-abi.h>
 
+#include <asm/div64.h>
+
 #define MTD_CHAR_MAJOR 90
 #define MTD_BLOCK_MAJOR 31
 #define MAX_MTD_DEVICES 32
 #define MTD_ERASE_DONE          0x08
 #define MTD_ERASE_FAILED        0x10
 
-#define MTD_FAIL_ADDR_UNKNOWN 0xffffffff
+#define MTD_FAIL_ADDR_UNKNOWN -1LL
 
 /* If the erase fails, fail_addr might indicate exactly which block failed.  If
    fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level or was not
    specific to any particular block. */
 struct erase_info {
        struct mtd_info *mtd;
-       u_int32_t addr;
-       u_int32_t len;
-       u_int32_t fail_addr;
+       uint64_t addr;
+       uint64_t len;
+       uint64_t fail_addr;
        u_long time;
        u_long retries;
-       u_int dev;
-       u_int cell;
+       unsigned dev;
+       unsigned cell;
        void (*callback) (struct erase_info *self);
        u_long priv;
        u_char state;
@@ -46,9 +48,9 @@ struct erase_info {
 };
 
 struct mtd_erase_region_info {
-       u_int32_t offset;                       /* At which this region starts, from the beginning of the MTD */
-       u_int32_t erasesize;            /* For this region */
-       u_int32_t numblocks;            /* Number of blocks of erasesize in this region */
+       uint64_t offset;                        /* At which this region starts, from the beginning of the MTD */
+       uint32_t erasesize;             /* For this region */
+       uint32_t numblocks;             /* Number of blocks of erasesize in this region */
        unsigned long *lockmap;         /* If keeping bitmap of locks */
 };
 
@@ -100,14 +102,14 @@ struct mtd_oob_ops {
 
 struct mtd_info {
        u_char type;
-       u_int32_t flags;
-       u_int32_t size;  // Total size of the MTD
+       uint32_t flags;
+       uint64_t size;   // Total size of the MTD
 
        /* "Major" erase size for the device. Naïve users may take this
         * to be the only erase size available, or may use the more detailed
         * information below if they desire
         */
-       u_int32_t erasesize;
+       uint32_t erasesize;
        /* Minimal writable flash unit size. In case of NOR flash it is 1 (even
         * though individual bits can be cleared), in case of NAND flash it is
         * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
@@ -115,10 +117,20 @@ struct mtd_info {
         * Any driver registering a struct mtd_info must ensure a writesize of
         * 1 or larger.
         */
-       u_int32_t writesize;
+       uint32_t writesize;
+
+       uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
+       uint32_t oobavail;  // Available OOB bytes per block
 
-       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
-       u_int32_t oobavail;  // Available OOB bytes per block
+       /*
+        * If erasesize is a power of 2 then the shift is stored in
+        * erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
+        */
+       unsigned int erasesize_shift;
+       unsigned int writesize_shift;
+       /* Masks based on erasesize_shift and writesize_shift */
+       unsigned int erasesize_mask;
+       unsigned int writesize_mask;
 
        // Kernel-only stuff starts here.
        const char *name;
@@ -190,8 +202,8 @@ struct mtd_info {
        void (*sync) (struct mtd_info *mtd);
 
        /* Chip-supported device locking */
-       int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
-       int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
+       int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
+       int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
 
        /* Power Management functions */
        int (*suspend) (struct mtd_info *mtd);
@@ -221,6 +233,35 @@ struct mtd_info {
        void (*put_device) (struct mtd_info *mtd);
 };
 
+static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
+{
+       if (mtd->erasesize_shift)
+               return sz >> mtd->erasesize_shift;
+       do_div(sz, mtd->erasesize);
+       return sz;
+}
+
+static inline uint32_t mtd_mod_by_eb(uint64_t sz, struct mtd_info *mtd)
+{
+       if (mtd->erasesize_shift)
+               return sz & mtd->erasesize_mask;
+       return do_div(sz, mtd->erasesize);
+}
+
+static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
+{
+       if (mtd->writesize_shift)
+               return sz >> mtd->writesize_shift;
+       do_div(sz, mtd->writesize);
+       return sz;
+}
+
+static inline uint32_t mtd_mod_by_ws(uint64_t sz, struct mtd_info *mtd)
+{
+       if (mtd->writesize_shift)
+               return sz & mtd->writesize_mask;
+       return do_div(sz, mtd->writesize);
+}
 
        /* Kernel-side ioctl definitions */
 
index 733d3f3b4eb8bbda4101a6a1a37eba84d424b9f0..db5b63da2a7e46d619a746233cf40b4a8397e4c9 100644 (file)
@@ -335,17 +335,12 @@ struct nand_buffers {
  * @erase_cmd:         [INTERN] erase command write function, selectable due to AND support
  * @scan_bbt:          [REPLACEABLE] function to scan bad block table
  * @chip_delay:                [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
- * @wq:                        [INTERN] wait queue to sleep on if a NAND operation is in progress
  * @state:             [INTERN] the current state of the NAND device
  * @oob_poi:           poison value buffer
  * @page_shift:                [INTERN] number of address bits in a page (column address bits)
  * @phys_erase_shift:  [INTERN] number of address bits in a physical eraseblock
  * @bbt_erase_shift:   [INTERN] number of address bits in a bbt entry
  * @chip_shift:                [INTERN] number of address bits in one chip
- * @datbuf:            [INTERN] internal buffer for one page + oob
- * @oobbuf:            [INTERN] oob buffer for one eraseblock
- * @oobdirty:          [INTERN] indicates that oob_buf must be reinitialized
- * @data_poi:          [INTERN] pointer to a data buffer
  * @options:           [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
  *                     special functionality. See the defines for further explanation
  * @badblockpos:       [INTERN] position of the bad block marker in the oob area
@@ -399,7 +394,7 @@ struct nand_chip {
        int             bbt_erase_shift;
        int             chip_shift;
        int             numchips;
-       unsigned long   chipsize;
+       uint64_t        chipsize;
        int             pagemask;
        int             pagebuf;
        int             subpagesize;
index c92b4d439609feec0640b953fedc502fd9a8890d..a45dd831b3f8ec0c468edf0890a8217dd80de29b 100644 (file)
@@ -36,9 +36,9 @@
 
 struct mtd_partition {
        char *name;                     /* identifier string */
-       u_int32_t size;                 /* partition size */
-       u_int32_t offset;               /* offset within the master MTD space */
-       u_int32_t mask_flags;           /* master MTD flags to mask out for this partition */
+       uint64_t size;                  /* partition size */
+       uint64_t offset;                /* offset within the master MTD space */
+       uint32_t mask_flags;            /* master MTD flags to mask out for this partition */
        struct nand_ecclayout *ecclayout;       /* out of band layout for this partition (NAND only)*/
        struct mtd_info **mtdp;         /* pointer to store the MTD object */
 };
diff --git a/include/linux/mtd/pfow.h b/include/linux/mtd/pfow.h
new file mode 100644 (file)
index 0000000..b730d4f
--- /dev/null
@@ -0,0 +1,159 @@
+/* Primary function overlay window definitions
+ * and service functions used by LPDDR chips
+ */
+#ifndef __LINUX_MTD_PFOW_H
+#define __LINUX_MTD_PFOW_H
+
+#include <linux/mtd/qinfo.h>
+
+/* PFOW registers addressing */
+/* Address of symbol "P" */
+#define PFOW_QUERY_STRING_P                    0x0000
+/* Address of symbol "F" */
+#define PFOW_QUERY_STRING_F                    0x0002
+/* Address of symbol "O" */
+#define PFOW_QUERY_STRING_O                    0x0004
+/* Address of symbol "W" */
+#define PFOW_QUERY_STRING_W                    0x0006
+/* Identification info for LPDDR chip */
+#define PFOW_MANUFACTURER_ID                   0x0020
+#define PFOW_DEVICE_ID                         0x0022
+/* Address in PFOW where prog buffer can can be found */
+#define PFOW_PROGRAM_BUFFER_OFFSET             0x0040
+/* Size of program buffer in words */
+#define PFOW_PROGRAM_BUFFER_SIZE               0x0042
+/* Address command code register */
+#define PFOW_COMMAND_CODE                      0x0080
+/* command data register */
+#define PFOW_COMMAND_DATA                      0x0084
+/* command address register lower address bits */
+#define PFOW_COMMAND_ADDRESS_L                 0x0088
+/* command address register upper address bits */
+#define PFOW_COMMAND_ADDRESS_H                 0x008a
+/* number of bytes to be proggrammed lower address bits */
+#define PFOW_DATA_COUNT_L                      0x0090
+/* number of bytes to be proggrammed higher address bits */
+#define PFOW_DATA_COUNT_H                      0x0092
+/* command execution register, the only possible value is 0x01 */
+#define PFOW_COMMAND_EXECUTE                   0x00c0
+/* 0x01 should be written at this address to clear buffer */
+#define PFOW_CLEAR_PROGRAM_BUFFER              0x00c4
+/* device program/erase suspend register */
+#define PFOW_PROGRAM_ERASE_SUSPEND             0x00c8
+/* device status register */
+#define PFOW_DSR                               0x00cc
+
+/* LPDDR memory device command codes */
+/* They are possible values of PFOW command code register */
+#define LPDDR_WORD_PROGRAM             0x0041
+#define LPDDR_BUFF_PROGRAM             0x00E9
+#define LPDDR_BLOCK_ERASE              0x0020
+#define LPDDR_LOCK_BLOCK               0x0061
+#define LPDDR_UNLOCK_BLOCK             0x0062
+#define LPDDR_READ_BLOCK_LOCK_STATUS   0x0065
+#define LPDDR_INFO_QUERY               0x0098
+#define LPDDR_READ_OTP                 0x0097
+#define LPDDR_PROG_OTP                 0x00C0
+#define LPDDR_RESUME                   0x00D0
+
+/* Defines possible value of PFOW command execution register */
+#define LPDDR_START_EXECUTION                  0x0001
+
+/* Defines possible value of PFOW program/erase suspend register */
+#define LPDDR_SUSPEND                          0x0001
+
+/* Possible values of PFOW device status register */
+/* access R - read; RC read & clearable */
+#define DSR_DPS                        (1<<1) /* RC; device protect status
+                                       * 0 - not protected 1 - locked */
+#define DSR_PSS                        (1<<2) /* R; program suspend status;
+                                       * 0-prog in progress/completed,
+                                       * 1- prog suspended */
+#define DSR_VPPS               (1<<3) /* RC; 0-Vpp OK, * 1-Vpp low */
+#define DSR_PROGRAM_STATUS     (1<<4) /* RC; 0-successful, 1-error */
+#define DSR_ERASE_STATUS       (1<<5) /* RC; erase or blank check status;
+                                       * 0-success erase/blank check,
+                                       * 1 blank check error */
+#define DSR_ESS                        (1<<6) /* R; erase suspend status;
+                                       * 0-erase in progress/complete,
+                                       * 1 erase suspended */
+#define DSR_READY_STATUS       (1<<7) /* R; Device status
+                                       * 0-busy,
+                                       * 1-ready */
+#define DSR_RPS                        (0x3<<8) /* RC;  region program status
+                                       * 00 - Success,
+                                       * 01-re-program attempt in region with
+                                       * object mode data,
+                                       * 10-object mode program w attempt in
+                                       * region with control mode data
+                                       * 11-attempt to program invalid half
+                                       * with 0x41 command */
+#define DSR_AOS                        (1<<12) /* RC; 1- AO related failure */
+#define DSR_AVAILABLE          (1<<15) /* R; Device availbility
+                                       * 1 - Device available
+                                       * 0 - not available */
+
+/* The superset of all possible error bits in DSR */
+#define DSR_ERR                        0x133A
+
+static inline void send_pfow_command(struct map_info *map,
+                               unsigned long cmd_code, unsigned long adr,
+                               unsigned long len, map_word *datum)
+{
+       int bits_per_chip = map_bankwidth(map) * 8;
+       int chipnum;
+       struct lpddr_private *lpddr = map->fldrv_priv;
+       chipnum = adr >> lpddr->chipshift;
+
+       map_write(map, CMD(cmd_code), map->pfow_base + PFOW_COMMAND_CODE);
+       map_write(map, CMD(adr & ((1<<bits_per_chip) - 1)),
+                               map->pfow_base + PFOW_COMMAND_ADDRESS_L);
+       map_write(map, CMD(adr>>bits_per_chip),
+                               map->pfow_base + PFOW_COMMAND_ADDRESS_H);
+       if (len) {
+               map_write(map, CMD(len & ((1<<bits_per_chip) - 1)),
+                                       map->pfow_base + PFOW_DATA_COUNT_L);
+               map_write(map, CMD(len>>bits_per_chip),
+                                       map->pfow_base + PFOW_DATA_COUNT_H);
+       }
+       if (datum)
+               map_write(map, *datum, map->pfow_base + PFOW_COMMAND_DATA);
+
+       /* Command execution start */
+       map_write(map, CMD(LPDDR_START_EXECUTION),
+                       map->pfow_base + PFOW_COMMAND_EXECUTE);
+}
+
+static inline void print_drs_error(unsigned dsr)
+{
+       int prog_status = (dsr & DSR_RPS) >> 8;
+
+       if (!(dsr & DSR_AVAILABLE))
+               printk(KERN_NOTICE"DSR.15: (0) Device not Available\n");
+       if (prog_status & 0x03)
+               printk(KERN_NOTICE"DSR.9,8: (11) Attempt to program invalid "
+                                               "half with 41h command\n");
+       else if (prog_status & 0x02)
+               printk(KERN_NOTICE"DSR.9,8: (10) Object Mode Program attempt "
+                                       "in region with Control Mode data\n");
+       else if (prog_status &  0x01)
+               printk(KERN_NOTICE"DSR.9,8: (01) Program attempt in region "
+                                               "with Object Mode data\n");
+       if (!(dsr & DSR_READY_STATUS))
+               printk(KERN_NOTICE"DSR.7: (0) Device is Busy\n");
+       if (dsr & DSR_ESS)
+               printk(KERN_NOTICE"DSR.6: (1) Erase Suspended\n");
+       if (dsr & DSR_ERASE_STATUS)
+               printk(KERN_NOTICE"DSR.5: (1) Erase/Blank check error\n");
+       if (dsr & DSR_PROGRAM_STATUS)
+               printk(KERN_NOTICE"DSR.4: (1) Program Error\n");
+       if (dsr & DSR_VPPS)
+               printk(KERN_NOTICE"DSR.3: (1) Vpp low detect, operation "
+                                       "aborted\n");
+       if (dsr & DSR_PSS)
+               printk(KERN_NOTICE"DSR.2: (1) Program suspended\n");
+       if (dsr & DSR_DPS)
+               printk(KERN_NOTICE"DSR.1: (1) Aborted Erase/Program attempt "
+                                       "on locked block\n");
+}
+#endif /* __LINUX_MTD_PFOW_H */
index c8e63a5ee72e1a0cbbffc06a02a327d2c0772ed5..76f7cabf07d322ade437ca757a1c6bd9d93b6107 100644 (file)
@@ -24,6 +24,7 @@ struct physmap_flash_data {
        unsigned int            width;
        void                    (*set_vpp)(struct map_info *, int);
        unsigned int            nr_parts;
+       unsigned int            pfow_base;
        struct mtd_partition    *parts;
 };
 
diff --git a/include/linux/mtd/qinfo.h b/include/linux/mtd/qinfo.h
new file mode 100644 (file)
index 0000000..7b3d487
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef __LINUX_MTD_QINFO_H
+#define __LINUX_MTD_QINFO_H
+
+#include <linux/mtd/map.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/flashchip.h>
+#include <linux/mtd/partitions.h>
+
+/* lpddr_private describes lpddr flash chip in memory map
+ * @ManufactId - Chip Manufacture ID
+ * @DevId - Chip Device ID
+ * @qinfo - pointer to qinfo records describing the chip
+ * @numchips - number of chips including virual RWW partitions
+ * @chipshift - Chip/partiton size 2^chipshift
+ * @chips - per-chip data structure
+ */
+struct lpddr_private {
+       uint16_t ManufactId;
+       uint16_t DevId;
+       struct qinfo_chip *qinfo;
+       int numchips;
+       unsigned long chipshift;
+       struct flchip chips[0];
+};
+
+/* qinfo_query_info structure contains request information for
+ * each qinfo record
+ * @major - major number of qinfo record
+ * @major - minor number of qinfo record
+ * @id_str - descriptive string to access the record
+ * @desc - detailed description for the qinfo record
+ */
+struct qinfo_query_info {
+       uint8_t major;
+       uint8_t minor;
+       char *id_str;
+       char *desc;
+};
+
+/*
+ * qinfo_chip structure contains necessary qinfo records data
+ * @DevSizeShift - Device size 2^n bytes
+ * @BufSizeShift - Program buffer size 2^n bytes
+ * @TotalBlocksNum - Total number of blocks
+ * @UniformBlockSizeShift - Uniform block size 2^UniformBlockSizeShift bytes
+ * @HWPartsNum - Number of hardware partitions
+ * @SuspEraseSupp - Suspend erase supported
+ * @SingleWordProgTime - Single word program 2^SingleWordProgTime u-sec
+ * @ProgBufferTime - Program buffer write 2^ProgBufferTime u-sec
+ * @BlockEraseTime - Block erase 2^BlockEraseTime m-sec
+ */
+struct qinfo_chip {
+       /* General device info */
+       uint16_t DevSizeShift;
+       uint16_t BufSizeShift;
+       /* Erase block information */
+       uint16_t TotalBlocksNum;
+       uint16_t UniformBlockSizeShift;
+       /* Partition information */
+       uint16_t HWPartsNum;
+       /* Optional features */
+       uint16_t SuspEraseSupp;
+       /* Operation typical time */
+       uint16_t SingleWordProgTime;
+       uint16_t ProgBufferTime;
+       uint16_t BlockEraseTime;
+};
+
+/* defines for fixup usage */
+#define LPDDR_MFR_ANY          0xffff
+#define LPDDR_ID_ANY           0xffff
+#define NUMONYX_MFGR_ID                0x0089
+#define R18_DEVICE_ID_1G       0x893c
+
+static inline map_word lpddr_build_cmd(u_long cmd, struct map_info *map)
+{
+       map_word val = { {0} };
+       val.x[0] = cmd;
+       return val;
+}
+
+#define CMD(x) lpddr_build_cmd(x, map)
+#define CMDVAL(cmd) cmd.x[0]
+
+struct mtd_info *lpddr_cmdset(struct map_info *);
+
+#endif
+
diff --git a/include/linux/mtd/sharpsl.h b/include/linux/mtd/sharpsl.h
new file mode 100644 (file)
index 0000000..25f4d2a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * SharpSL NAND support
+ *
+ * Copyright (C) 2008 Dmitry Baryshkov
+ *
+ * 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/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+struct sharpsl_nand_platform_data {
+       struct nand_bbt_descr   *badblock_pattern;
+       struct nand_ecclayout   *ecc_layout;
+       struct mtd_partition    *partitions;
+       unsigned int            nr_partitions;
+};